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 "RCP_HOST"
30
31 #include "host/rcp_host.hpp"
32
33 #include <assert.h>
34 #include <limits.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include <openthread/backbone_router_ftd.h>
39 #include <openthread/border_routing.h>
40 #include <openthread/dataset.h>
41 #include <openthread/dnssd_server.h>
42 #include <openthread/link_metrics.h>
43 #include <openthread/logging.h>
44 #include <openthread/nat64.h>
45 #include <openthread/srp_server.h>
46 #include <openthread/tasklet.h>
47 #include <openthread/thread.h>
48 #include <openthread/thread_ftd.h>
49 #include <openthread/trel.h>
50 #include <openthread/platform/logging.h>
51 #include <openthread/platform/misc.h>
52 #include <openthread/platform/radio.h>
53 #include <openthread/platform/settings.h>
54
55 #include "common/code_utils.hpp"
56 #include "common/logging.hpp"
57 #include "common/types.hpp"
58 #if OTBR_ENABLE_FEATURE_FLAGS
59 #include "proto/feature_flag.pb.h"
60 #endif
61
62 namespace otbr {
63 namespace Host {
64
65 static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
66 static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2
67 static const uint16_t kThreadVersion13 = 4; ///< Thread Version 1.3
68 static const uint16_t kThreadVersion14 = 5; ///< Thread Version 1.4
69
70 // =============================== OtNetworkProperties ===============================
71
OtNetworkProperties(void)72 OtNetworkProperties::OtNetworkProperties(void)
73 : mInstance(nullptr)
74 {
75 }
76
GetDeviceRole(void) const77 otDeviceRole OtNetworkProperties::GetDeviceRole(void) const
78 {
79 return otThreadGetDeviceRole(mInstance);
80 }
81
Ip6IsEnabled(void) const82 bool OtNetworkProperties::Ip6IsEnabled(void) const
83 {
84 return otIp6IsEnabled(mInstance);
85 }
86
GetPartitionId(void) const87 uint32_t OtNetworkProperties::GetPartitionId(void) const
88 {
89 return otThreadGetPartitionId(mInstance);
90 }
91
GetDatasetActiveTlvs(otOperationalDatasetTlvs & aDatasetTlvs) const92 void OtNetworkProperties::GetDatasetActiveTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
93 {
94 otError error = otDatasetGetActiveTlvs(mInstance, &aDatasetTlvs);
95
96 if (error != OT_ERROR_NONE)
97 {
98 aDatasetTlvs.mLength = 0;
99 memset(aDatasetTlvs.mTlvs, 0, sizeof(aDatasetTlvs.mTlvs));
100 }
101 }
102
GetDatasetPendingTlvs(otOperationalDatasetTlvs & aDatasetTlvs) const103 void OtNetworkProperties::GetDatasetPendingTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
104 {
105 otError error = otDatasetGetPendingTlvs(mInstance, &aDatasetTlvs);
106
107 if (error != OT_ERROR_NONE)
108 {
109 aDatasetTlvs.mLength = 0;
110 memset(aDatasetTlvs.mTlvs, 0, sizeof(aDatasetTlvs.mTlvs));
111 }
112 }
113
SetInstance(otInstance * aInstance)114 void OtNetworkProperties::SetInstance(otInstance *aInstance)
115 {
116 mInstance = aInstance;
117 }
118
119 // =============================== RcpHost ===============================
120
RcpHost(const char * aInterfaceName,const std::vector<const char * > & aRadioUrls,const char * aBackboneInterfaceName,bool aDryRun,bool aEnableAutoAttach)121 RcpHost::RcpHost(const char *aInterfaceName,
122 const std::vector<const char *> &aRadioUrls,
123 const char *aBackboneInterfaceName,
124 bool aDryRun,
125 bool aEnableAutoAttach)
126 : mInstance(nullptr)
127 , mEnableAutoAttach(aEnableAutoAttach)
128 , mThreadEnabledState(ThreadEnabledState::kStateDisabled)
129 {
130 VerifyOrDie(aRadioUrls.size() <= OT_PLATFORM_CONFIG_MAX_RADIO_URLS, "Too many Radio URLs!");
131
132 memset(&mConfig, 0, sizeof(mConfig));
133
134 mConfig.mInterfaceName = aInterfaceName;
135 mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
136 mConfig.mDryRun = aDryRun;
137
138 for (const char *url : aRadioUrls)
139 {
140 mConfig.mCoprocessorUrls.mUrls[mConfig.mCoprocessorUrls.mNum++] = url;
141 }
142 mConfig.mSpeedUpFactor = 1;
143 }
144
~RcpHost(void)145 RcpHost::~RcpHost(void)
146 {
147 // Make sure OpenThread Instance was gracefully de-initialized.
148 assert(mInstance == nullptr);
149 }
150
ConvertToOtbrLogLevel(otLogLevel aLogLevel)151 otbrLogLevel RcpHost::ConvertToOtbrLogLevel(otLogLevel aLogLevel)
152 {
153 otbrLogLevel otbrLogLevel;
154
155 switch (aLogLevel)
156 {
157 case OT_LOG_LEVEL_NONE:
158 otbrLogLevel = OTBR_LOG_EMERG;
159 break;
160 case OT_LOG_LEVEL_CRIT:
161 otbrLogLevel = OTBR_LOG_CRIT;
162 break;
163 case OT_LOG_LEVEL_WARN:
164 otbrLogLevel = OTBR_LOG_WARNING;
165 break;
166 case OT_LOG_LEVEL_NOTE:
167 otbrLogLevel = OTBR_LOG_NOTICE;
168 break;
169 case OT_LOG_LEVEL_INFO:
170 otbrLogLevel = OTBR_LOG_INFO;
171 break;
172 case OT_LOG_LEVEL_DEBG:
173 default:
174 otbrLogLevel = OTBR_LOG_DEBUG;
175 break;
176 }
177
178 return otbrLogLevel;
179 }
180
181 #if OTBR_ENABLE_FEATURE_FLAGS
182 /* Converts ProtoLogLevel to otbrLogLevel */
ConvertProtoToOtbrLogLevel(ProtoLogLevel aProtoLogLevel)183 otbrLogLevel ConvertProtoToOtbrLogLevel(ProtoLogLevel aProtoLogLevel)
184 {
185 otbrLogLevel otbrLogLevel;
186
187 switch (aProtoLogLevel)
188 {
189 case PROTO_LOG_EMERG:
190 otbrLogLevel = OTBR_LOG_EMERG;
191 break;
192 case PROTO_LOG_ALERT:
193 otbrLogLevel = OTBR_LOG_ALERT;
194 break;
195 case PROTO_LOG_CRIT:
196 otbrLogLevel = OTBR_LOG_CRIT;
197 break;
198 case PROTO_LOG_ERR:
199 otbrLogLevel = OTBR_LOG_ERR;
200 break;
201 case PROTO_LOG_WARNING:
202 otbrLogLevel = OTBR_LOG_WARNING;
203 break;
204 case PROTO_LOG_NOTICE:
205 otbrLogLevel = OTBR_LOG_NOTICE;
206 break;
207 case PROTO_LOG_INFO:
208 otbrLogLevel = OTBR_LOG_INFO;
209 break;
210 case PROTO_LOG_DEBUG:
211 default:
212 otbrLogLevel = OTBR_LOG_DEBUG;
213 break;
214 }
215
216 return otbrLogLevel;
217 }
218 #endif
219
SetOtbrAndOtLogLevel(otbrLogLevel aLevel)220 otError RcpHost::SetOtbrAndOtLogLevel(otbrLogLevel aLevel)
221 {
222 otError error = OT_ERROR_NONE;
223 otbrLogSetLevel(aLevel);
224 error = otLoggingSetLevel(ConvertToOtLogLevel(aLevel));
225 return error;
226 }
227
Init(void)228 void RcpHost::Init(void)
229 {
230 otbrError error = OTBR_ERROR_NONE;
231 otLogLevel level = ConvertToOtLogLevel(otbrLogGetLevel());
232
233 #if OTBR_ENABLE_FEATURE_FLAGS && OTBR_ENABLE_TREL
234 FeatureFlagList featureFlagList;
235 #endif
236
237 VerifyOrExit(otLoggingSetLevel(level) == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
238
239 mInstance = otSysInit(&mConfig);
240 assert(mInstance != nullptr);
241
242 {
243 otError result = otSetStateChangedCallback(mInstance, &RcpHost::HandleStateChanged, this);
244
245 agent::ThreadHelper::LogOpenThreadResult("Set state callback", result);
246 VerifyOrExit(result == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
247 }
248
249 #if OTBR_ENABLE_FEATURE_FLAGS && OTBR_ENABLE_TREL
250 // Enable/Disable trel according to feature flag default value.
251 otTrelSetEnabled(mInstance, featureFlagList.enable_trel());
252 #endif
253
254 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
255 #if OTBR_ENABLE_SRP_SERVER_AUTO_ENABLE_MODE
256 // Let SRP server use auto-enable mode. The auto-enable mode delegates the control of SRP server to the Border
257 // Routing Manager. SRP server automatically starts when bi-directional connectivity is ready.
258 otSrpServerSetAutoEnableMode(mInstance, /* aEnabled */ true);
259 #else
260 otSrpServerSetEnabled(mInstance, /* aEnabled */ true);
261 #endif
262 #endif
263
264 #if !OTBR_ENABLE_FEATURE_FLAGS
265 // Bring up all features when feature flags is not supported.
266 #if OTBR_ENABLE_NAT64
267 otNat64SetEnabled(mInstance, /* aEnabled */ true);
268 #endif
269 #if OTBR_ENABLE_DNS_UPSTREAM_QUERY
270 otDnssdUpstreamQuerySetEnabled(mInstance, /* aEnabled */ true);
271 #endif
272 #if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
273 otBorderRoutingDhcp6PdSetEnabled(mInstance, /* aEnabled */ true);
274 #endif
275 #endif // OTBR_ENABLE_FEATURE_FLAGS
276
277 mThreadHelper = MakeUnique<otbr::agent::ThreadHelper>(mInstance, this);
278
279 OtNetworkProperties::SetInstance(mInstance);
280
281 exit:
282 SuccessOrDie(error, "Failed to initialize the RCP Host!");
283 }
284
285 #if OTBR_ENABLE_FEATURE_FLAGS
ApplyFeatureFlagList(const FeatureFlagList & aFeatureFlagList)286 otError RcpHost::ApplyFeatureFlagList(const FeatureFlagList &aFeatureFlagList)
287 {
288 otError error = OT_ERROR_NONE;
289 // Save a cached copy of feature flags for debugging purpose.
290 mAppliedFeatureFlagListBytes = aFeatureFlagList.SerializeAsString();
291
292 #if OTBR_ENABLE_NAT64
293 otNat64SetEnabled(mInstance, aFeatureFlagList.enable_nat64());
294 #endif
295
296 if (aFeatureFlagList.enable_detailed_logging())
297 {
298 error = SetOtbrAndOtLogLevel(ConvertProtoToOtbrLogLevel(aFeatureFlagList.detailed_logging_level()));
299 }
300 else
301 {
302 error = SetOtbrAndOtLogLevel(otbrLogGetDefaultLevel());
303 }
304
305 #if OTBR_ENABLE_TREL
306 otTrelSetEnabled(mInstance, aFeatureFlagList.enable_trel());
307 #endif
308 #if OTBR_ENABLE_DNS_UPSTREAM_QUERY
309 otDnssdUpstreamQuerySetEnabled(mInstance, aFeatureFlagList.enable_dns_upstream_query());
310 #endif
311 #if OTBR_ENABLE_DHCP6_PD
312 otBorderRoutingDhcp6PdSetEnabled(mInstance, aFeatureFlagList.enable_dhcp6_pd());
313 #endif
314 #if OTBR_ENABLE_LINK_METRICS_TELEMETRY
315 otLinkMetricsManagerSetEnabled(mInstance, aFeatureFlagList.enable_link_metrics_manager());
316 #endif
317
318 return error;
319 }
320 #endif
321
Deinit(void)322 void RcpHost::Deinit(void)
323 {
324 assert(mInstance != nullptr);
325
326 otSysDeinit();
327 mInstance = nullptr;
328
329 OtNetworkProperties::SetInstance(nullptr);
330 mThreadStateChangedCallbacks.clear();
331 mThreadEnabledStateChangedCallbacks.clear();
332 mResetHandlers.clear();
333
334 mJoinReceiver = nullptr;
335 mSetThreadEnabledReceiver = nullptr;
336 mScheduleMigrationReceiver = nullptr;
337 mDetachGracefullyCallbacks.clear();
338 }
339
HandleStateChanged(otChangedFlags aFlags)340 void RcpHost::HandleStateChanged(otChangedFlags aFlags)
341 {
342 for (auto &stateCallback : mThreadStateChangedCallbacks)
343 {
344 stateCallback(aFlags);
345 }
346
347 mThreadHelper->StateChangedCallback(aFlags);
348
349 if ((aFlags & OT_CHANGED_THREAD_ROLE) && IsAttached() && mJoinReceiver != nullptr)
350 {
351 otbrLogInfo("Join succeeded");
352 SafeInvokeAndClear(mJoinReceiver, OT_ERROR_NONE, "Join succeeded");
353 }
354 }
355
Update(MainloopContext & aMainloop)356 void RcpHost::Update(MainloopContext &aMainloop)
357 {
358 if (otTaskletsArePending(mInstance))
359 {
360 aMainloop.mTimeout = ToTimeval(Microseconds::zero());
361 }
362
363 otSysMainloopUpdate(mInstance, &aMainloop);
364 }
365
Process(const MainloopContext & aMainloop)366 void RcpHost::Process(const MainloopContext &aMainloop)
367 {
368 otTaskletsProcess(mInstance);
369
370 otSysMainloopProcess(mInstance, &aMainloop);
371
372 if (IsAutoAttachEnabled() && mThreadHelper->TryResumeNetwork() == OT_ERROR_NONE)
373 {
374 DisableAutoAttach();
375 }
376 }
377
IsAutoAttachEnabled(void)378 bool RcpHost::IsAutoAttachEnabled(void)
379 {
380 return mEnableAutoAttach;
381 }
382
DisableAutoAttach(void)383 void RcpHost::DisableAutoAttach(void)
384 {
385 mEnableAutoAttach = false;
386 }
387
PostTimerTask(Milliseconds aDelay,TaskRunner::Task<void> aTask)388 void RcpHost::PostTimerTask(Milliseconds aDelay, TaskRunner::Task<void> aTask)
389 {
390 mTaskRunner.Post(std::move(aDelay), std::move(aTask));
391 }
392
RegisterResetHandler(std::function<void (void)> aHandler)393 void RcpHost::RegisterResetHandler(std::function<void(void)> aHandler)
394 {
395 mResetHandlers.emplace_back(std::move(aHandler));
396 }
397
AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)398 void RcpHost::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
399 {
400 mThreadStateChangedCallbacks.emplace_back(std::move(aCallback));
401 }
402
AddThreadEnabledStateChangedCallback(ThreadEnabledStateCallback aCallback)403 void RcpHost::AddThreadEnabledStateChangedCallback(ThreadEnabledStateCallback aCallback)
404 {
405 mThreadEnabledStateChangedCallbacks.push_back(aCallback);
406 }
407
Reset(void)408 void RcpHost::Reset(void)
409 {
410 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
411
412 otSysDeinit();
413 mInstance = nullptr;
414
415 Init();
416 for (auto &handler : mResetHandlers)
417 {
418 handler();
419 }
420 mEnableAutoAttach = true;
421 }
422
GetThreadVersion(void)423 const char *RcpHost::GetThreadVersion(void)
424 {
425 const char *version;
426
427 switch (otThreadGetVersion())
428 {
429 case kThreadVersion11:
430 version = "1.1.1";
431 break;
432 case kThreadVersion12:
433 version = "1.2.0";
434 break;
435 case kThreadVersion13:
436 version = "1.3.0";
437 break;
438 case kThreadVersion14:
439 version = "1.4.0";
440 break;
441 default:
442 otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
443 exit(-1);
444 }
445 return version;
446 }
447
noNeedRejoin(const otOperationalDatasetTlvs & aLhs,const otOperationalDatasetTlvs & aRhs)448 static bool noNeedRejoin(const otOperationalDatasetTlvs &aLhs, const otOperationalDatasetTlvs &aRhs)
449 {
450 bool result = false;
451
452 otOperationalDataset lhsDataset;
453 otOperationalDataset rhsDataset;
454
455 SuccessOrExit(otDatasetParseTlvs(&aLhs, &lhsDataset));
456 SuccessOrExit(otDatasetParseTlvs(&aRhs, &rhsDataset));
457
458 result =
459 (lhsDataset.mChannel == rhsDataset.mChannel) &&
460 (memcmp(lhsDataset.mNetworkKey.m8, rhsDataset.mNetworkKey.m8, sizeof(lhsDataset.mNetworkKey)) == 0) &&
461 (memcmp(lhsDataset.mExtendedPanId.m8, rhsDataset.mExtendedPanId.m8, sizeof(lhsDataset.mExtendedPanId)) == 0);
462
463 exit:
464 return result;
465 }
466
Join(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs,const AsyncResultReceiver & aReceiver)467 void RcpHost::Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aReceiver)
468 {
469 otError error = OT_ERROR_NONE;
470 std::string errorMsg;
471 bool receiveResultHere = true;
472 otOperationalDatasetTlvs curDatasetTlvs;
473
474 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
475 VerifyOrExit(mThreadEnabledState != ThreadEnabledState::kStateDisabling, error = OT_ERROR_BUSY,
476 errorMsg = "Thread is disabling");
477 VerifyOrExit(mThreadEnabledState == ThreadEnabledState::kStateEnabled, error = OT_ERROR_INVALID_STATE,
478 errorMsg = "Thread is not enabled");
479
480 otbrLogInfo("Start joining...");
481
482 error = otDatasetGetActiveTlvs(mInstance, &curDatasetTlvs);
483 if (error == OT_ERROR_NONE && noNeedRejoin(aActiveOpDatasetTlvs, curDatasetTlvs) && IsAttached())
484 {
485 // Do not leave and re-join if this device has already joined the same network. This can help elimilate
486 // unnecessary connectivity and topology disruption and save the time for re-joining. It's more useful for use
487 // cases where Thread networks are dynamically brought up and torn down (e.g. Thread on mobile phones).
488 SuccessOrExit(error = otDatasetSetActiveTlvs(mInstance, &aActiveOpDatasetTlvs),
489 errorMsg = "Failed to set Active Operational Dataset");
490 errorMsg = "Already Joined the target network";
491 ExitNow();
492 }
493
494 if (GetDeviceRole() != OT_DEVICE_ROLE_DISABLED)
495 {
496 ThreadDetachGracefully([aActiveOpDatasetTlvs, aReceiver, this] {
497 ConditionalErasePersistentInfo(true);
498 Join(aActiveOpDatasetTlvs, aReceiver);
499 });
500 receiveResultHere = false;
501 ExitNow();
502 }
503
504 SuccessOrExit(error = otDatasetSetActiveTlvs(mInstance, &aActiveOpDatasetTlvs),
505 errorMsg = "Failed to set Active Operational Dataset");
506
507 // TODO(b/273160198): check how we can implement join as a child
508 SuccessOrExit(error = otIp6SetEnabled(mInstance, true), errorMsg = "Failed to bring up Thread interface");
509 SuccessOrExit(error = otThreadSetEnabled(mInstance, true), errorMsg = "Failed to bring up Thread stack");
510
511 // Abort an ongoing join()
512 if (mJoinReceiver != nullptr)
513 {
514 SafeInvoke(mJoinReceiver, OT_ERROR_ABORT, "Join() is aborted");
515 }
516 mJoinReceiver = aReceiver;
517 receiveResultHere = false;
518
519 exit:
520 if (receiveResultHere)
521 {
522 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
523 }
524 }
525
Leave(bool aEraseDataset,const AsyncResultReceiver & aReceiver)526 void RcpHost::Leave(bool aEraseDataset, const AsyncResultReceiver &aReceiver)
527 {
528 otError error = OT_ERROR_NONE;
529 std::string errorMsg;
530 bool receiveResultHere = true;
531
532 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
533 VerifyOrExit(mThreadEnabledState != ThreadEnabledState::kStateDisabling, error = OT_ERROR_BUSY,
534 errorMsg = "Thread is disabling");
535
536 if (mThreadEnabledState == ThreadEnabledState::kStateDisabled)
537 {
538 ConditionalErasePersistentInfo(aEraseDataset);
539 ExitNow();
540 }
541
542 ThreadDetachGracefully([aEraseDataset, aReceiver, this] {
543 ConditionalErasePersistentInfo(aEraseDataset);
544 if (aReceiver)
545 {
546 aReceiver(OT_ERROR_NONE, "");
547 }
548 });
549
550 exit:
551 if (receiveResultHere)
552 {
553 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
554 }
555 }
556
ScheduleMigration(const otOperationalDatasetTlvs & aPendingOpDatasetTlvs,const AsyncResultReceiver aReceiver)557 void RcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
558 const AsyncResultReceiver aReceiver)
559 {
560 otError error = OT_ERROR_NONE;
561 std::string errorMsg;
562 otOperationalDataset emptyDataset;
563
564 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
565
566 VerifyOrExit(mThreadEnabledState != ThreadEnabledState::kStateDisabling, error = OT_ERROR_BUSY,
567 errorMsg = "Thread is disabling");
568 VerifyOrExit(mThreadEnabledState == ThreadEnabledState::kStateEnabled, error = OT_ERROR_INVALID_STATE,
569 errorMsg = "Thread is disabled");
570
571 VerifyOrExit(IsAttached(), error = OT_ERROR_INVALID_STATE, errorMsg = "Device is detached");
572
573 // TODO: check supported channel mask
574
575 SuccessOrExit(error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, aPendingOpDatasetTlvs.mTlvs,
576 static_cast<uint8_t>(aPendingOpDatasetTlvs.mLength),
577 SendMgmtPendingSetCallback, this),
578 errorMsg = "Failed to send MGMT_PENDING_SET.req");
579
580 exit:
581 if (error != OT_ERROR_NONE)
582 {
583 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
584 }
585 else
586 {
587 // otDatasetSendMgmtPendingSet() returns OT_ERROR_BUSY if it has already been called before but the
588 // callback hasn't been invoked. So we can guarantee that mMigrationReceiver is always nullptr here
589 assert(mScheduleMigrationReceiver == nullptr);
590 mScheduleMigrationReceiver = aReceiver;
591 }
592 }
593
SendMgmtPendingSetCallback(otError aError,void * aContext)594 void RcpHost::SendMgmtPendingSetCallback(otError aError, void *aContext)
595 {
596 static_cast<RcpHost *>(aContext)->SendMgmtPendingSetCallback(aError);
597 }
598
SendMgmtPendingSetCallback(otError aError)599 void RcpHost::SendMgmtPendingSetCallback(otError aError)
600 {
601 SafeInvokeAndClear(mScheduleMigrationReceiver, aError, "");
602 }
603
SetThreadEnabled(bool aEnabled,const AsyncResultReceiver aReceiver)604 void RcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver)
605 {
606 otError error = OT_ERROR_NONE;
607 std::string errorMsg = "";
608 bool receiveResultHere = true;
609
610 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
611 VerifyOrExit(mThreadEnabledState != ThreadEnabledState::kStateDisabling, error = OT_ERROR_BUSY,
612 errorMsg = "Thread is disabling");
613
614 if (aEnabled)
615 {
616 otOperationalDatasetTlvs datasetTlvs;
617
618 if (mThreadEnabledState == ThreadEnabledState::kStateEnabled)
619 {
620 ExitNow();
621 }
622
623 if (otDatasetGetActiveTlvs(mInstance, &datasetTlvs) != OT_ERROR_NOT_FOUND && datasetTlvs.mLength > 0 &&
624 otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_DISABLED)
625 {
626 SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
627 SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
628 }
629 UpdateThreadEnabledState(ThreadEnabledState::kStateEnabled);
630 }
631 else
632 {
633 UpdateThreadEnabledState(ThreadEnabledState::kStateDisabling);
634
635 ThreadDetachGracefully([this](void) { DisableThreadAfterDetach(); });
636 mSetThreadEnabledReceiver = aReceiver;
637 receiveResultHere = false;
638 }
639
640 exit:
641 if (receiveResultHere)
642 {
643 mTaskRunner.Post([aReceiver, error, errorMsg](void) { SafeInvoke(aReceiver, error, errorMsg); });
644 }
645 }
646
GetChannelMasks(const ChannelMasksReceiver & aReceiver,const AsyncResultReceiver & aErrReceiver)647 void RcpHost::GetChannelMasks(const ChannelMasksReceiver &aReceiver, const AsyncResultReceiver &aErrReceiver)
648 {
649 otError error = OT_ERROR_NONE;
650 uint32_t supportedChannelMask;
651 uint32_t preferredChannelMask;
652
653 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE);
654
655 supportedChannelMask = otLinkGetSupportedChannelMask(mInstance);
656 preferredChannelMask = otPlatRadioGetPreferredChannelMask(mInstance);
657
658 exit:
659 if (error == OT_ERROR_NONE)
660 {
661 mTaskRunner.Post([aReceiver, supportedChannelMask, preferredChannelMask](void) {
662 aReceiver(supportedChannelMask, preferredChannelMask);
663 });
664 }
665 else
666 {
667 mTaskRunner.Post([aErrReceiver, error](void) { aErrReceiver(error, "OT is not initialized"); });
668 }
669 }
670
671 #if OTBR_ENABLE_POWER_CALIBRATION
SetChannelMaxPowers(const std::vector<ChannelMaxPower> & aChannelMaxPowers,const AsyncResultReceiver & aReceiver)672 void RcpHost::SetChannelMaxPowers(const std::vector<ChannelMaxPower> &aChannelMaxPowers,
673 const AsyncResultReceiver &aReceiver)
674 {
675 otError error = OT_ERROR_NONE;
676 std::string errorMsg;
677
678 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
679
680 for (ChannelMaxPower channelMaxPower : aChannelMaxPowers)
681 {
682 VerifyOrExit((channelMaxPower.mChannel >= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN) &&
683 (channelMaxPower.mChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX),
684 error = OT_ERROR_INVALID_ARGS, errorMsg = "The channel is invalid");
685 }
686
687 for (ChannelMaxPower channelMaxPower : aChannelMaxPowers)
688 {
689 otbrLogInfo("Set channel max power: channel=%u, maxPower=%u", static_cast<uint32_t>(channelMaxPower.mChannel),
690 static_cast<uint32_t>(channelMaxPower.mMaxPower));
691 SuccessOrExit(error = otPlatRadioSetChannelTargetPower(
692 mInstance, static_cast<uint8_t>(channelMaxPower.mChannel), channelMaxPower.mMaxPower),
693 errorMsg = "Failed to set channel max power");
694 }
695
696 exit:
697 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
698 }
699 #endif // OTBR_ENABLE_POWER_CALIBRATION
700
ThreadDetachGracefully(const DetachGracefullyCallback & aCallback)701 void RcpHost::ThreadDetachGracefully(const DetachGracefullyCallback &aCallback)
702 {
703 mDetachGracefullyCallbacks.push_back(aCallback);
704
705 // Ignores the OT_ERROR_BUSY error if a detach has already been requested
706 OT_UNUSED_VARIABLE(otThreadDetachGracefully(mInstance, ThreadDetachGracefullyCallback, this));
707 }
708
ThreadDetachGracefullyCallback(void * aContext)709 void RcpHost::ThreadDetachGracefullyCallback(void *aContext)
710 {
711 static_cast<RcpHost *>(aContext)->ThreadDetachGracefullyCallback();
712 }
713
ThreadDetachGracefullyCallback(void)714 void RcpHost::ThreadDetachGracefullyCallback(void)
715 {
716 SafeInvokeAndClear(mJoinReceiver, OT_ERROR_ABORT, "Aborted by leave/disable operation");
717 SafeInvokeAndClear(mScheduleMigrationReceiver, OT_ERROR_ABORT, "Aborted by leave/disable operation");
718
719 for (auto &callback : mDetachGracefullyCallbacks)
720 {
721 callback();
722 }
723 mDetachGracefullyCallbacks.clear();
724 }
725
ConditionalErasePersistentInfo(bool aErase)726 void RcpHost::ConditionalErasePersistentInfo(bool aErase)
727 {
728 if (aErase)
729 {
730 OT_UNUSED_VARIABLE(otInstanceErasePersistentInfo(mInstance));
731 }
732 }
733
DisableThreadAfterDetach(void)734 void RcpHost::DisableThreadAfterDetach(void)
735 {
736 otError error = OT_ERROR_NONE;
737 std::string errorMsg;
738
739 SuccessOrExit(error = otThreadSetEnabled(mInstance, false), errorMsg = "Failed to disable Thread stack");
740 SuccessOrExit(error = otIp6SetEnabled(mInstance, false), errorMsg = "Failed to disable Thread interface");
741
742 UpdateThreadEnabledState(ThreadEnabledState::kStateDisabled);
743
744 exit:
745 SafeInvokeAndClear(mSetThreadEnabledReceiver, error, errorMsg);
746 }
747
SetCountryCode(const std::string & aCountryCode,const AsyncResultReceiver & aReceiver)748 void RcpHost::SetCountryCode(const std::string &aCountryCode, const AsyncResultReceiver &aReceiver)
749 {
750 static constexpr int kCountryCodeLength = 2;
751 otError error = OT_ERROR_NONE;
752 std::string errorMsg;
753 uint16_t countryCode;
754
755 VerifyOrExit((aCountryCode.length() == kCountryCodeLength) && isalpha(aCountryCode[0]) && isalpha(aCountryCode[1]),
756 error = OT_ERROR_INVALID_ARGS, errorMsg = "The country code is invalid");
757
758 otbrLogInfo("Set country code: %c%c", aCountryCode[0], aCountryCode[1]);
759 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
760
761 countryCode = static_cast<uint16_t>((aCountryCode[0] << 8) | aCountryCode[1]);
762 SuccessOrExit(error = otLinkSetRegion(mInstance, countryCode), errorMsg = "Failed to set the country code");
763
764 exit:
765 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
766 }
767
IsAttached(void)768 bool RcpHost::IsAttached(void)
769 {
770 otDeviceRole role = GetDeviceRole();
771
772 return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
773 }
774
UpdateThreadEnabledState(ThreadEnabledState aState)775 void RcpHost::UpdateThreadEnabledState(ThreadEnabledState aState)
776 {
777 mThreadEnabledState = aState;
778
779 for (auto &callback : mThreadEnabledStateChangedCallbacks)
780 {
781 callback(mThreadEnabledState);
782 }
783 }
784
785 /*
786 * Provide, if required an "otPlatLog()" function
787 */
otPlatLog(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,...)788 extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
789 {
790 OT_UNUSED_VARIABLE(aLogRegion);
791
792 otbrLogLevel otbrLogLevel = RcpHost::ConvertToOtbrLogLevel(aLogLevel);
793
794 va_list ap;
795 va_start(ap, aFormat);
796 otbrLogvNoFilter(otbrLogLevel, aFormat, ap);
797 va_end(ap);
798 }
799
otPlatLogHandleLevelChanged(otLogLevel aLogLevel)800 extern "C" void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
801 {
802 otbrLogSetLevel(RcpHost::ConvertToOtbrLogLevel(aLogLevel));
803 otbrLogInfo("OpenThread log level changed to %d", aLogLevel);
804 }
805
806 } // namespace Host
807 } // namespace otbr
808