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 "ncp/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 Ncp {
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 {
129 VerifyOrDie(aRadioUrls.size() <= OT_PLATFORM_CONFIG_MAX_RADIO_URLS, "Too many Radio URLs!");
130
131 memset(&mConfig, 0, sizeof(mConfig));
132
133 mConfig.mInterfaceName = aInterfaceName;
134 mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
135 mConfig.mDryRun = aDryRun;
136
137 for (const char *url : aRadioUrls)
138 {
139 mConfig.mCoprocessorUrls.mUrls[mConfig.mCoprocessorUrls.mNum++] = url;
140 }
141 mConfig.mSpeedUpFactor = 1;
142 }
143
~RcpHost(void)144 RcpHost::~RcpHost(void)
145 {
146 // Make sure OpenThread Instance was gracefully de-initialized.
147 assert(mInstance == nullptr);
148 }
149
ConvertToOtbrLogLevel(otLogLevel aLogLevel)150 otbrLogLevel RcpHost::ConvertToOtbrLogLevel(otLogLevel aLogLevel)
151 {
152 otbrLogLevel otbrLogLevel;
153
154 switch (aLogLevel)
155 {
156 case OT_LOG_LEVEL_NONE:
157 otbrLogLevel = OTBR_LOG_EMERG;
158 break;
159 case OT_LOG_LEVEL_CRIT:
160 otbrLogLevel = OTBR_LOG_CRIT;
161 break;
162 case OT_LOG_LEVEL_WARN:
163 otbrLogLevel = OTBR_LOG_WARNING;
164 break;
165 case OT_LOG_LEVEL_NOTE:
166 otbrLogLevel = OTBR_LOG_NOTICE;
167 break;
168 case OT_LOG_LEVEL_INFO:
169 otbrLogLevel = OTBR_LOG_INFO;
170 break;
171 case OT_LOG_LEVEL_DEBG:
172 default:
173 otbrLogLevel = OTBR_LOG_DEBUG;
174 break;
175 }
176
177 return otbrLogLevel;
178 }
179
180 #if OTBR_ENABLE_FEATURE_FLAGS
181 /* Converts ProtoLogLevel to otbrLogLevel */
ConvertProtoToOtbrLogLevel(ProtoLogLevel aProtoLogLevel)182 otbrLogLevel ConvertProtoToOtbrLogLevel(ProtoLogLevel aProtoLogLevel)
183 {
184 otbrLogLevel otbrLogLevel;
185
186 switch (aProtoLogLevel)
187 {
188 case PROTO_LOG_EMERG:
189 otbrLogLevel = OTBR_LOG_EMERG;
190 break;
191 case PROTO_LOG_ALERT:
192 otbrLogLevel = OTBR_LOG_ALERT;
193 break;
194 case PROTO_LOG_CRIT:
195 otbrLogLevel = OTBR_LOG_CRIT;
196 break;
197 case PROTO_LOG_ERR:
198 otbrLogLevel = OTBR_LOG_ERR;
199 break;
200 case PROTO_LOG_WARNING:
201 otbrLogLevel = OTBR_LOG_WARNING;
202 break;
203 case PROTO_LOG_NOTICE:
204 otbrLogLevel = OTBR_LOG_NOTICE;
205 break;
206 case PROTO_LOG_INFO:
207 otbrLogLevel = OTBR_LOG_INFO;
208 break;
209 case PROTO_LOG_DEBUG:
210 default:
211 otbrLogLevel = OTBR_LOG_DEBUG;
212 break;
213 }
214
215 return otbrLogLevel;
216 }
217 #endif
218
SetOtbrAndOtLogLevel(otbrLogLevel aLevel)219 otError RcpHost::SetOtbrAndOtLogLevel(otbrLogLevel aLevel)
220 {
221 otError error = OT_ERROR_NONE;
222 otbrLogSetLevel(aLevel);
223 error = otLoggingSetLevel(ConvertToOtLogLevel(aLevel));
224 return error;
225 }
226
Init(void)227 void RcpHost::Init(void)
228 {
229 otbrError error = OTBR_ERROR_NONE;
230 otLogLevel level = ConvertToOtLogLevel(otbrLogGetLevel());
231
232 #if OTBR_ENABLE_FEATURE_FLAGS && OTBR_ENABLE_TREL
233 FeatureFlagList featureFlagList;
234 #endif
235
236 VerifyOrExit(otLoggingSetLevel(level) == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
237
238 mInstance = otSysInit(&mConfig);
239 assert(mInstance != nullptr);
240
241 {
242 otError result = otSetStateChangedCallback(mInstance, &RcpHost::HandleStateChanged, this);
243
244 agent::ThreadHelper::LogOpenThreadResult("Set state callback", result);
245 VerifyOrExit(result == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
246 }
247
248 #if OTBR_ENABLE_FEATURE_FLAGS && OTBR_ENABLE_TREL
249 // Enable/Disable trel according to feature flag default value.
250 otTrelSetEnabled(mInstance, featureFlagList.enable_trel());
251 #endif
252
253 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
254 #if OTBR_ENABLE_SRP_SERVER_AUTO_ENABLE_MODE
255 // Let SRP server use auto-enable mode. The auto-enable mode delegates the control of SRP server to the Border
256 // Routing Manager. SRP server automatically starts when bi-directional connectivity is ready.
257 otSrpServerSetAutoEnableMode(mInstance, /* aEnabled */ true);
258 #else
259 otSrpServerSetEnabled(mInstance, /* aEnabled */ true);
260 #endif
261 #endif
262
263 #if !OTBR_ENABLE_FEATURE_FLAGS
264 // Bring up all features when feature flags is not supported.
265 #if OTBR_ENABLE_NAT64
266 otNat64SetEnabled(mInstance, /* aEnabled */ true);
267 #endif
268 #if OTBR_ENABLE_DNS_UPSTREAM_QUERY
269 otDnssdUpstreamQuerySetEnabled(mInstance, /* aEnabled */ true);
270 #endif
271 #if OTBR_ENABLE_DHCP6_PD
272 otBorderRoutingDhcp6PdSetEnabled(mInstance, /* aEnabled */ true);
273 #endif
274 #endif // OTBR_ENABLE_FEATURE_FLAGS
275
276 mThreadHelper = MakeUnique<otbr::agent::ThreadHelper>(mInstance, this);
277
278 OtNetworkProperties::SetInstance(mInstance);
279
280 exit:
281 SuccessOrDie(error, "Failed to initialize the RCP Host!");
282 }
283
284 #if OTBR_ENABLE_FEATURE_FLAGS
ApplyFeatureFlagList(const FeatureFlagList & aFeatureFlagList)285 otError RcpHost::ApplyFeatureFlagList(const FeatureFlagList &aFeatureFlagList)
286 {
287 otError error = OT_ERROR_NONE;
288 // Save a cached copy of feature flags for debugging purpose.
289 mAppliedFeatureFlagListBytes = aFeatureFlagList.SerializeAsString();
290
291 #if OTBR_ENABLE_NAT64
292 otNat64SetEnabled(mInstance, aFeatureFlagList.enable_nat64());
293 #endif
294
295 if (aFeatureFlagList.enable_detailed_logging())
296 {
297 error = SetOtbrAndOtLogLevel(ConvertProtoToOtbrLogLevel(aFeatureFlagList.detailed_logging_level()));
298 }
299 else
300 {
301 error = SetOtbrAndOtLogLevel(otbrLogGetDefaultLevel());
302 }
303
304 #if OTBR_ENABLE_TREL
305 otTrelSetEnabled(mInstance, aFeatureFlagList.enable_trel());
306 #endif
307 #if OTBR_ENABLE_DNS_UPSTREAM_QUERY
308 otDnssdUpstreamQuerySetEnabled(mInstance, aFeatureFlagList.enable_dns_upstream_query());
309 #endif
310 #if OTBR_ENABLE_DHCP6_PD
311 otBorderRoutingDhcp6PdSetEnabled(mInstance, aFeatureFlagList.enable_dhcp6_pd());
312 #endif
313 #if OTBR_ENABLE_LINK_METRICS_TELEMETRY
314 otLinkMetricsManagerSetEnabled(mInstance, aFeatureFlagList.enable_link_metrics_manager());
315 #endif
316
317 return error;
318 }
319 #endif
320
Deinit(void)321 void RcpHost::Deinit(void)
322 {
323 assert(mInstance != nullptr);
324
325 otSysDeinit();
326 mInstance = nullptr;
327
328 OtNetworkProperties::SetInstance(nullptr);
329 mThreadStateChangedCallbacks.clear();
330 mResetHandlers.clear();
331
332 mSetThreadEnabledReceiver = nullptr;
333 mScheduleMigrationReceiver = nullptr;
334 }
335
HandleStateChanged(otChangedFlags aFlags)336 void RcpHost::HandleStateChanged(otChangedFlags aFlags)
337 {
338 for (auto &stateCallback : mThreadStateChangedCallbacks)
339 {
340 stateCallback(aFlags);
341 }
342
343 mThreadHelper->StateChangedCallback(aFlags);
344 }
345
Update(MainloopContext & aMainloop)346 void RcpHost::Update(MainloopContext &aMainloop)
347 {
348 if (otTaskletsArePending(mInstance))
349 {
350 aMainloop.mTimeout = ToTimeval(Microseconds::zero());
351 }
352
353 otSysMainloopUpdate(mInstance, &aMainloop);
354 }
355
Process(const MainloopContext & aMainloop)356 void RcpHost::Process(const MainloopContext &aMainloop)
357 {
358 otTaskletsProcess(mInstance);
359
360 otSysMainloopProcess(mInstance, &aMainloop);
361
362 if (IsAutoAttachEnabled() && mThreadHelper->TryResumeNetwork() == OT_ERROR_NONE)
363 {
364 DisableAutoAttach();
365 }
366 }
367
IsAutoAttachEnabled(void)368 bool RcpHost::IsAutoAttachEnabled(void)
369 {
370 return mEnableAutoAttach;
371 }
372
DisableAutoAttach(void)373 void RcpHost::DisableAutoAttach(void)
374 {
375 mEnableAutoAttach = false;
376 }
377
PostTimerTask(Milliseconds aDelay,TaskRunner::Task<void> aTask)378 void RcpHost::PostTimerTask(Milliseconds aDelay, TaskRunner::Task<void> aTask)
379 {
380 mTaskRunner.Post(std::move(aDelay), std::move(aTask));
381 }
382
RegisterResetHandler(std::function<void (void)> aHandler)383 void RcpHost::RegisterResetHandler(std::function<void(void)> aHandler)
384 {
385 mResetHandlers.emplace_back(std::move(aHandler));
386 }
387
AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)388 void RcpHost::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
389 {
390 mThreadStateChangedCallbacks.emplace_back(std::move(aCallback));
391 }
392
Reset(void)393 void RcpHost::Reset(void)
394 {
395 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
396
397 otSysDeinit();
398 mInstance = nullptr;
399
400 Init();
401 for (auto &handler : mResetHandlers)
402 {
403 handler();
404 }
405 mEnableAutoAttach = true;
406 }
407
GetThreadVersion(void)408 const char *RcpHost::GetThreadVersion(void)
409 {
410 const char *version;
411
412 switch (otThreadGetVersion())
413 {
414 case kThreadVersion11:
415 version = "1.1.1";
416 break;
417 case kThreadVersion12:
418 version = "1.2.0";
419 break;
420 case kThreadVersion13:
421 version = "1.3.0";
422 break;
423 case kThreadVersion14:
424 version = "1.4.0";
425 break;
426 default:
427 otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
428 exit(-1);
429 }
430 return version;
431 }
432
Join(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs,const AsyncResultReceiver & aReceiver)433 void RcpHost::Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aReceiver)
434 {
435 OT_UNUSED_VARIABLE(aActiveOpDatasetTlvs);
436
437 // TODO: Implement Join under RCP mode.
438 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
439 }
440
Leave(const AsyncResultReceiver & aReceiver)441 void RcpHost::Leave(const AsyncResultReceiver &aReceiver)
442 {
443 // TODO: Implement Leave under RCP mode.
444 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
445 }
446
ScheduleMigration(const otOperationalDatasetTlvs & aPendingOpDatasetTlvs,const AsyncResultReceiver aReceiver)447 void RcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
448 const AsyncResultReceiver aReceiver)
449 {
450 otError error = OT_ERROR_NONE;
451 std::string errorMsg;
452 otOperationalDataset emptyDataset;
453
454 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
455 VerifyOrExit(IsAttached(), error = OT_ERROR_FAILED,
456 errorMsg = "Cannot schedule migration when this device is detached");
457
458 // TODO: check supported channel mask
459
460 SuccessOrExit(error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, aPendingOpDatasetTlvs.mTlvs,
461 static_cast<uint8_t>(aPendingOpDatasetTlvs.mLength),
462 SendMgmtPendingSetCallback, this),
463 errorMsg = "Failed to send MGMT_PENDING_SET.req");
464
465 exit:
466 if (error != OT_ERROR_NONE)
467 {
468 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
469 }
470 else
471 {
472 // otDatasetSendMgmtPendingSet() returns OT_ERROR_BUSY if it has already been called before but the
473 // callback hasn't been invoked. So we can guarantee that mMigrationReceiver is always nullptr here
474 assert(mScheduleMigrationReceiver == nullptr);
475 mScheduleMigrationReceiver = aReceiver;
476 }
477 }
478
SendMgmtPendingSetCallback(otError aError,void * aContext)479 void RcpHost::SendMgmtPendingSetCallback(otError aError, void *aContext)
480 {
481 static_cast<RcpHost *>(aContext)->SendMgmtPendingSetCallback(aError);
482 }
483
SendMgmtPendingSetCallback(otError aError)484 void RcpHost::SendMgmtPendingSetCallback(otError aError)
485 {
486 SafeInvokeAndClear(mScheduleMigrationReceiver, aError, "");
487 }
488
SetThreadEnabled(bool aEnabled,const AsyncResultReceiver aReceiver)489 void RcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver)
490 {
491 otError error = OT_ERROR_NONE;
492 bool receiveResultHere = true;
493
494 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE);
495 VerifyOrExit(mSetThreadEnabledReceiver == nullptr, error = OT_ERROR_BUSY);
496
497 if (aEnabled)
498 {
499 otOperationalDatasetTlvs datasetTlvs;
500
501 if (otDatasetGetActiveTlvs(mInstance, &datasetTlvs) != OT_ERROR_NOT_FOUND && datasetTlvs.mLength > 0 &&
502 otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_DISABLED)
503 {
504 SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
505 SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
506 }
507 }
508 else
509 {
510 SuccessOrExit(error = otThreadDetachGracefully(mInstance, DisableThreadAfterDetach, this));
511 mSetThreadEnabledReceiver = aReceiver;
512 receiveResultHere = false;
513 }
514
515 exit:
516 if (receiveResultHere)
517 {
518 mTaskRunner.Post([aReceiver, error](void) { aReceiver(error, ""); });
519 }
520 }
521
GetChannelMasks(const ChannelMasksReceiver & aReceiver,const AsyncResultReceiver & aErrReceiver)522 void RcpHost::GetChannelMasks(const ChannelMasksReceiver &aReceiver, const AsyncResultReceiver &aErrReceiver)
523 {
524 otError error = OT_ERROR_NONE;
525 uint32_t supportedChannelMask;
526 uint32_t preferredChannelMask;
527
528 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE);
529
530 supportedChannelMask = otLinkGetSupportedChannelMask(mInstance);
531 preferredChannelMask = otPlatRadioGetPreferredChannelMask(mInstance);
532
533 exit:
534 if (error == OT_ERROR_NONE)
535 {
536 mTaskRunner.Post([aReceiver, supportedChannelMask, preferredChannelMask](void) {
537 aReceiver(supportedChannelMask, preferredChannelMask);
538 });
539 }
540 else
541 {
542 mTaskRunner.Post([aErrReceiver, error](void) { aErrReceiver(error, "OT is not initialized"); });
543 }
544 }
545
SetChannelMaxPowers(const std::vector<ChannelMaxPower> & aChannelMaxPowers,const AsyncResultReceiver & aReceiver)546 void RcpHost::SetChannelMaxPowers(const std::vector<ChannelMaxPower> &aChannelMaxPowers,
547 const AsyncResultReceiver &aReceiver)
548 {
549 otError error = OT_ERROR_NONE;
550 std::string errorMsg;
551
552 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
553
554 for (ChannelMaxPower channelMaxPower : aChannelMaxPowers)
555 {
556 VerifyOrExit((channelMaxPower.mChannel >= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN) &&
557 (channelMaxPower.mChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX),
558 error = OT_ERROR_INVALID_ARGS, errorMsg = "The channel is invalid");
559 }
560
561 for (ChannelMaxPower channelMaxPower : aChannelMaxPowers)
562 {
563 otbrLogInfo("Set channel max power: channel=%u, maxPower=%u", static_cast<uint32_t>(channelMaxPower.mChannel),
564 static_cast<uint32_t>(channelMaxPower.mMaxPower));
565 SuccessOrExit(error = otPlatRadioSetChannelTargetPower(
566 mInstance, static_cast<uint8_t>(channelMaxPower.mChannel), channelMaxPower.mMaxPower),
567 errorMsg = "Failed to set channel max power");
568 }
569
570 exit:
571 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
572 }
573
DisableThreadAfterDetach(void * aContext)574 void RcpHost::DisableThreadAfterDetach(void *aContext)
575 {
576 static_cast<RcpHost *>(aContext)->DisableThreadAfterDetach();
577 }
578
DisableThreadAfterDetach(void)579 void RcpHost::DisableThreadAfterDetach(void)
580 {
581 otError error = OT_ERROR_NONE;
582 std::string errorMsg;
583
584 SuccessOrExit(error = otThreadSetEnabled(mInstance, false), errorMsg = "Failed to disable Thread stack");
585 SuccessOrExit(error = otIp6SetEnabled(mInstance, false), errorMsg = "Failed to disable Thread interface");
586
587 exit:
588 SafeInvokeAndClear(mSetThreadEnabledReceiver, error, errorMsg);
589 }
590
SetCountryCode(const std::string & aCountryCode,const AsyncResultReceiver & aReceiver)591 void RcpHost::SetCountryCode(const std::string &aCountryCode, const AsyncResultReceiver &aReceiver)
592 {
593 static constexpr int kCountryCodeLength = 2;
594 otError error = OT_ERROR_NONE;
595 std::string errorMsg;
596 uint16_t countryCode;
597
598 VerifyOrExit((aCountryCode.length() == kCountryCodeLength) && isalpha(aCountryCode[0]) && isalpha(aCountryCode[1]),
599 error = OT_ERROR_INVALID_ARGS, errorMsg = "The country code is invalid");
600
601 otbrLogInfo("Set country code: %c%c", aCountryCode[0], aCountryCode[1]);
602 VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
603
604 countryCode = static_cast<uint16_t>((aCountryCode[0] << 8) | aCountryCode[1]);
605 SuccessOrExit(error = otLinkSetRegion(mInstance, countryCode), errorMsg = "Failed to set the country code");
606
607 exit:
608 mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
609 }
610
IsAttached(void)611 bool RcpHost::IsAttached(void)
612 {
613 otDeviceRole role = GetDeviceRole();
614
615 return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
616 }
617
618 /*
619 * Provide, if required an "otPlatLog()" function
620 */
otPlatLog(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,...)621 extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
622 {
623 OT_UNUSED_VARIABLE(aLogRegion);
624
625 otbrLogLevel otbrLogLevel = RcpHost::ConvertToOtbrLogLevel(aLogLevel);
626
627 va_list ap;
628 va_start(ap, aFormat);
629 otbrLogvNoFilter(otbrLogLevel, aFormat, ap);
630 va_end(ap);
631 }
632
otPlatLogHandleLevelChanged(otLogLevel aLogLevel)633 extern "C" void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
634 {
635 otbrLogSetLevel(RcpHost::ConvertToOtbrLogLevel(aLogLevel));
636 otbrLogInfo("OpenThread log level changed to %d", aLogLevel);
637 }
638
639 } // namespace Ncp
640 } // namespace otbr
641