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 "NcpSpinel"
30
31 #include "ncp_spinel.hpp"
32
33 #include <stdarg.h>
34
35 #include <algorithm>
36
37 #include <openthread/dataset.h>
38 #include <openthread/thread.h>
39
40 #include "common/code_utils.hpp"
41 #include "common/logging.hpp"
42 #include "lib/spinel/spinel.h"
43 #include "lib/spinel/spinel_decoder.hpp"
44 #include "lib/spinel/spinel_driver.hpp"
45 #include "lib/spinel/spinel_helper.hpp"
46
47 namespace otbr {
48 namespace Ncp {
49
50 static constexpr char kSpinelDataUnpackFormat[] = "CiiD";
51
NcpSpinel(void)52 NcpSpinel::NcpSpinel(void)
53 : mSpinelDriver(nullptr)
54 , mCmdTidsInUse(0)
55 , mCmdNextTid(1)
56 , mNcpBuffer(mTxBuffer, kTxBufferSize)
57 , mEncoder(mNcpBuffer)
58 , mIid(SPINEL_HEADER_INVALID_IID)
59 , mPropsObserver(nullptr)
60 {
61 std::fill_n(mWaitingKeyTable, SPINEL_PROP_LAST_STATUS, sizeof(mWaitingKeyTable));
62 memset(mCmdTable, 0, sizeof(mCmdTable));
63 }
64
Init(ot::Spinel::SpinelDriver & aSpinelDriver,PropsObserver & aObserver)65 void NcpSpinel::Init(ot::Spinel::SpinelDriver &aSpinelDriver, PropsObserver &aObserver)
66 {
67 mSpinelDriver = &aSpinelDriver;
68 mPropsObserver = &aObserver;
69 mIid = mSpinelDriver->GetIid();
70 mSpinelDriver->SetFrameHandler(&HandleReceivedFrame, &HandleSavedFrame, this);
71 }
72
Deinit(void)73 void NcpSpinel::Deinit(void)
74 {
75 mSpinelDriver = nullptr;
76 mIp6AddressTableCallback = nullptr;
77 mNetifStateChangedCallback = nullptr;
78 }
79
SpinelDataUnpack(const uint8_t * aDataIn,spinel_size_t aDataLen,const char * aPackFormat,...)80 otbrError NcpSpinel::SpinelDataUnpack(const uint8_t *aDataIn, spinel_size_t aDataLen, const char *aPackFormat, ...)
81 {
82 otbrError error = OTBR_ERROR_NONE;
83 spinel_ssize_t unpacked;
84 va_list args;
85
86 va_start(args, aPackFormat);
87 unpacked = spinel_datatype_vunpack(aDataIn, aDataLen, aPackFormat, args);
88 va_end(args);
89
90 VerifyOrExit(unpacked > 0, error = OTBR_ERROR_PARSE);
91
92 exit:
93 return error;
94 }
95
DatasetSetActiveTlvs(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs,AsyncTaskPtr aAsyncTask)96 void NcpSpinel::DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, AsyncTaskPtr aAsyncTask)
97 {
98 otError error = OT_ERROR_NONE;
99 EncodingFunc encodingFunc = [this, &aActiveOpDatasetTlvs] {
100 return mEncoder.WriteData(aActiveOpDatasetTlvs.mTlvs, aActiveOpDatasetTlvs.mLength);
101 };
102
103 VerifyOrExit(mDatasetSetActiveTask == nullptr, error = OT_ERROR_BUSY);
104
105 SuccessOrExit(error = SetProperty(SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS, encodingFunc));
106 mDatasetSetActiveTask = aAsyncTask;
107
108 exit:
109 if (error != OT_ERROR_NONE)
110 {
111 mTaskRunner.Post([aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to set active dataset!"); });
112 }
113 }
114
DatasetMgmtSetPending(std::shared_ptr<otOperationalDatasetTlvs> aPendingOpDatasetTlvsPtr,AsyncTaskPtr aAsyncTask)115 void NcpSpinel::DatasetMgmtSetPending(std::shared_ptr<otOperationalDatasetTlvs> aPendingOpDatasetTlvsPtr,
116 AsyncTaskPtr aAsyncTask)
117 {
118 otError error = OT_ERROR_NONE;
119 EncodingFunc encodingFunc = [this, aPendingOpDatasetTlvsPtr] {
120 return mEncoder.WriteData(aPendingOpDatasetTlvsPtr->mTlvs, aPendingOpDatasetTlvsPtr->mLength);
121 };
122
123 VerifyOrExit(mDatasetMgmtSetPendingTask == nullptr, error = OT_ERROR_BUSY);
124
125 SuccessOrExit(error = SetProperty(SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS, encodingFunc));
126 mDatasetMgmtSetPendingTask = aAsyncTask;
127
128 exit:
129 if (error != OT_ERROR_NONE)
130 {
131 mTaskRunner.Post([aAsyncTask, error] { aAsyncTask->SetResult(error, "Failed to set pending dataset!"); });
132 }
133 }
134
Ip6SetEnabled(bool aEnable,AsyncTaskPtr aAsyncTask)135 void NcpSpinel::Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask)
136 {
137 otError error = OT_ERROR_NONE;
138 EncodingFunc encodingFunc = [this, aEnable] { return mEncoder.WriteBool(aEnable); };
139
140 VerifyOrExit(mIp6SetEnabledTask == nullptr, error = OT_ERROR_BUSY);
141
142 SuccessOrExit(error = SetProperty(SPINEL_PROP_NET_IF_UP, encodingFunc));
143 mIp6SetEnabledTask = aAsyncTask;
144
145 exit:
146 if (error != OT_ERROR_NONE)
147 {
148 mTaskRunner.Post(
149 [aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to enable the network interface!"); });
150 }
151 return;
152 }
153
Ip6Send(const uint8_t * aData,uint16_t aLength)154 otbrError NcpSpinel::Ip6Send(const uint8_t *aData, uint16_t aLength)
155 {
156 // TODO: Impelement this function.
157 OTBR_UNUSED_VARIABLE(aData);
158 OTBR_UNUSED_VARIABLE(aLength);
159
160 return OTBR_ERROR_NONE;
161 }
162
ThreadSetEnabled(bool aEnable,AsyncTaskPtr aAsyncTask)163 void NcpSpinel::ThreadSetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask)
164 {
165 otError error = OT_ERROR_NONE;
166 EncodingFunc encodingFunc = [this, aEnable] { return mEncoder.WriteBool(aEnable); };
167
168 VerifyOrExit(mThreadSetEnabledTask == nullptr, error = OT_ERROR_BUSY);
169
170 SuccessOrExit(error = SetProperty(SPINEL_PROP_NET_STACK_UP, encodingFunc));
171 mThreadSetEnabledTask = aAsyncTask;
172
173 exit:
174 if (error != OT_ERROR_NONE)
175 {
176 mTaskRunner.Post(
177 [aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to enable the Thread network!"); });
178 }
179 return;
180 }
181
ThreadDetachGracefully(AsyncTaskPtr aAsyncTask)182 void NcpSpinel::ThreadDetachGracefully(AsyncTaskPtr aAsyncTask)
183 {
184 otError error = OT_ERROR_NONE;
185 EncodingFunc encodingFunc = [] { return OT_ERROR_NONE; };
186
187 VerifyOrExit(mThreadDetachGracefullyTask == nullptr, error = OT_ERROR_BUSY);
188
189 SuccessOrExit(error = SetProperty(SPINEL_PROP_NET_LEAVE_GRACEFULLY, encodingFunc));
190 mThreadDetachGracefullyTask = aAsyncTask;
191
192 exit:
193 if (error != OT_ERROR_NONE)
194 {
195 mTaskRunner.Post([aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to detach gracefully!"); });
196 }
197 return;
198 }
199
ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask)200 void NcpSpinel::ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask)
201 {
202 otError error = OT_ERROR_NONE;
203 spinel_tid_t tid = GetNextTid();
204
205 VerifyOrExit(mThreadErasePersistentInfoTask == nullptr, error = OT_ERROR_BUSY);
206
207 SuccessOrExit(error = mSpinelDriver->SendCommand(SPINEL_CMD_NET_CLEAR, SPINEL_PROP_LAST_STATUS, tid));
208
209 mWaitingKeyTable[tid] = SPINEL_PROP_LAST_STATUS;
210 mCmdTable[tid] = SPINEL_CMD_NET_CLEAR;
211 mThreadErasePersistentInfoTask = aAsyncTask;
212
213 exit:
214 if (error != OT_ERROR_NONE)
215 {
216 FreeTidTableItem(tid);
217 mTaskRunner.Post(
218 [aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to erase persistent info!"); });
219 }
220 }
221
HandleReceivedFrame(const uint8_t * aFrame,uint16_t aLength,uint8_t aHeader,bool & aSave,void * aContext)222 void NcpSpinel::HandleReceivedFrame(const uint8_t *aFrame,
223 uint16_t aLength,
224 uint8_t aHeader,
225 bool &aSave,
226 void *aContext)
227 {
228 static_cast<NcpSpinel *>(aContext)->HandleReceivedFrame(aFrame, aLength, aHeader, aSave);
229 }
230
HandleReceivedFrame(const uint8_t * aFrame,uint16_t aLength,uint8_t aHeader,bool & aShouldSaveFrame)231 void NcpSpinel::HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aShouldSaveFrame)
232 {
233 spinel_tid_t tid = SPINEL_HEADER_GET_TID(aHeader);
234
235 if (tid == 0)
236 {
237 HandleNotification(aFrame, aLength);
238 }
239 else if (tid < kMaxTids)
240 {
241 HandleResponse(tid, aFrame, aLength);
242 }
243 else
244 {
245 otbrLogCrit("Received unexpected tid: %u", tid);
246 }
247
248 aShouldSaveFrame = false;
249 }
250
HandleSavedFrame(const uint8_t * aFrame,uint16_t aLength,void * aContext)251 void NcpSpinel::HandleSavedFrame(const uint8_t *aFrame, uint16_t aLength, void *aContext)
252 {
253 /* Intentionally Empty */
254 OT_UNUSED_VARIABLE(aFrame);
255 OT_UNUSED_VARIABLE(aLength);
256 OT_UNUSED_VARIABLE(aContext);
257 }
258
HandleNotification(const uint8_t * aFrame,uint16_t aLength)259 void NcpSpinel::HandleNotification(const uint8_t *aFrame, uint16_t aLength)
260 {
261 spinel_prop_key_t key;
262 spinel_size_t len = 0;
263 uint8_t *data = nullptr;
264 uint32_t cmd;
265 uint8_t header;
266 otbrError error = OTBR_ERROR_NONE;
267
268 SuccessOrExit(error = SpinelDataUnpack(aFrame, aLength, kSpinelDataUnpackFormat, &header, &cmd, &key, &data, &len));
269 VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OTBR_ERROR_PARSE);
270 VerifyOrExit(cmd == SPINEL_CMD_PROP_VALUE_IS);
271 HandleValueIs(key, data, static_cast<uint16_t>(len));
272
273 exit:
274 otbrLogResult(error, "HandleNotification: %s", __FUNCTION__);
275 }
276
HandleResponse(spinel_tid_t aTid,const uint8_t * aFrame,uint16_t aLength)277 void NcpSpinel::HandleResponse(spinel_tid_t aTid, const uint8_t *aFrame, uint16_t aLength)
278 {
279 spinel_prop_key_t key;
280 spinel_size_t len = 0;
281 uint8_t *data = nullptr;
282 uint32_t cmd;
283 uint8_t header;
284 otbrError error = OTBR_ERROR_NONE;
285 FailureHandler failureHandler = nullptr;
286
287 SuccessOrExit(error = SpinelDataUnpack(aFrame, aLength, kSpinelDataUnpackFormat, &header, &cmd, &key, &data, &len));
288
289 VerifyOrExit(cmd == SPINEL_CMD_PROP_VALUE_IS, error = OTBR_ERROR_INVALID_STATE);
290
291 switch (mCmdTable[aTid])
292 {
293 case SPINEL_CMD_PROP_VALUE_SET:
294 {
295 error = HandleResponseForPropSet(aTid, key, data, len);
296 break;
297 }
298 case SPINEL_CMD_NET_CLEAR:
299 {
300 spinel_status_t status = SPINEL_STATUS_OK;
301
302 SuccessOrExit(error = SpinelDataUnpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status));
303 CallAndClear(mThreadErasePersistentInfoTask, ot::Spinel::SpinelStatusToOtError(status));
304 break;
305 }
306 default:
307 break;
308 }
309
310 exit:
311 if (error == OTBR_ERROR_INVALID_STATE)
312 {
313 otbrLogCrit("Received unexpected response with (cmd:%u, key:%u), waiting (cmd:%u, key:%u) for tid:%u", cmd, key,
314 mCmdTable[aTid], mWaitingKeyTable[aTid], aTid);
315 }
316 else if (error == OTBR_ERROR_PARSE)
317 {
318 otbrLogCrit("Error parsing response with tid:%u", aTid);
319 }
320 FreeTidTableItem(aTid);
321 }
322
HandleValueIs(spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)323 void NcpSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength)
324 {
325 otbrError error = OTBR_ERROR_NONE;
326
327 switch (aKey)
328 {
329 case SPINEL_PROP_LAST_STATUS:
330 {
331 spinel_status_t status = SPINEL_STATUS_OK;
332
333 SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status));
334
335 otbrLogInfo("NCP last status: %s", spinel_status_to_cstr(status));
336 break;
337 }
338
339 case SPINEL_PROP_NET_ROLE:
340 {
341 spinel_net_role_t role = SPINEL_NET_ROLE_DISABLED;
342 otDeviceRole deviceRole;
343
344 SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S, &role));
345
346 deviceRole = SpinelRoleToDeviceRole(role);
347 mPropsObserver->SetDeviceRole(deviceRole);
348
349 otbrLogInfo("Device role changed to %s", otThreadDeviceRoleToString(deviceRole));
350 break;
351 }
352
353 case SPINEL_PROP_NET_LEAVE_GRACEFULLY:
354 {
355 CallAndClear(mThreadDetachGracefullyTask, OT_ERROR_NONE);
356 break;
357 }
358
359 case SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS:
360 {
361 spinel_status_t status = SPINEL_STATUS_OK;
362
363 SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status));
364 CallAndClear(mDatasetMgmtSetPendingTask, ot::Spinel::SpinelStatusToOtError(status));
365 break;
366 }
367
368 case SPINEL_PROP_IPV6_ADDRESS_TABLE:
369 {
370 std::vector<Ip6AddressInfo> addressInfoTable;
371
372 VerifyOrExit(ParseIp6AddressTable(aBuffer, aLength, addressInfoTable) == OT_ERROR_NONE,
373 error = OTBR_ERROR_PARSE);
374 SafeInvoke(mIp6AddressTableCallback, addressInfoTable);
375 break;
376 }
377
378 case SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE:
379 {
380 std::vector<Ip6Address> addressTable;
381
382 VerifyOrExit(ParseIp6MulticastAddresses(aBuffer, aLength, addressTable) == OT_ERROR_NONE,
383 error = OTBR_ERROR_PARSE);
384 SafeInvoke(mIp6MulticastAddressTableCallback, addressTable);
385 break;
386 }
387
388 case SPINEL_PROP_NET_IF_UP:
389 {
390 bool isUp;
391 SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &isUp));
392 SafeInvoke(mNetifStateChangedCallback, isUp);
393 break;
394 }
395
396 default:
397 otbrLogWarning("Received uncognized key: %u", aKey);
398 break;
399 }
400
401 exit:
402 otbrLogResult(error, "NcpSpinel: %s", __FUNCTION__);
403 return;
404 }
405
HandleResponseForPropSet(spinel_tid_t aTid,spinel_prop_key_t aKey,const uint8_t * aData,uint16_t aLength)406 otbrError NcpSpinel::HandleResponseForPropSet(spinel_tid_t aTid,
407 spinel_prop_key_t aKey,
408 const uint8_t *aData,
409 uint16_t aLength)
410 {
411 OTBR_UNUSED_VARIABLE(aData);
412 OTBR_UNUSED_VARIABLE(aLength);
413
414 otbrError error = OTBR_ERROR_NONE;
415
416 switch (mWaitingKeyTable[aTid])
417 {
418 case SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS:
419 VerifyOrExit(aKey == SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS, error = OTBR_ERROR_INVALID_STATE);
420 CallAndClear(mDatasetSetActiveTask, OT_ERROR_NONE);
421 {
422 otOperationalDatasetTlvs datasetTlvs;
423 VerifyOrExit(ParseOperationalDatasetTlvs(aData, aLength, datasetTlvs) == OT_ERROR_NONE,
424 error = OTBR_ERROR_PARSE);
425 mPropsObserver->SetDatasetActiveTlvs(datasetTlvs);
426 }
427 break;
428
429 case SPINEL_PROP_NET_IF_UP:
430 VerifyOrExit(aKey == SPINEL_PROP_NET_IF_UP, error = OTBR_ERROR_INVALID_STATE);
431 CallAndClear(mIp6SetEnabledTask, OT_ERROR_NONE);
432 {
433 bool isUp;
434 SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_BOOL_S, &isUp));
435 SafeInvoke(mNetifStateChangedCallback, isUp);
436 }
437 break;
438
439 case SPINEL_PROP_NET_STACK_UP:
440 VerifyOrExit(aKey == SPINEL_PROP_NET_STACK_UP, error = OTBR_ERROR_INVALID_STATE);
441 CallAndClear(mThreadSetEnabledTask, OT_ERROR_NONE);
442 break;
443
444 case SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS:
445 if (aKey == SPINEL_PROP_LAST_STATUS)
446 { // Failed case
447 spinel_status_t status = SPINEL_STATUS_OK;
448
449 SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status));
450 CallAndClear(mDatasetMgmtSetPendingTask, ot::Spinel::SpinelStatusToOtError(status));
451 }
452 else if (aKey != SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS)
453 {
454 ExitNow(error = OTBR_ERROR_INVALID_STATE);
455 }
456 break;
457
458 default:
459 VerifyOrExit(aKey == mWaitingKeyTable[aTid], error = OTBR_ERROR_INVALID_STATE);
460 break;
461 }
462
463 exit:
464 return error;
465 }
466
GetNextTid(void)467 spinel_tid_t NcpSpinel::GetNextTid(void)
468 {
469 spinel_tid_t tid = mCmdNextTid;
470
471 while (((1 << tid) & mCmdTidsInUse) != 0)
472 {
473 tid = SPINEL_GET_NEXT_TID(tid);
474
475 if (tid == mCmdNextTid)
476 {
477 // We looped back to `mCmdNextTid` indicating that all
478 // TIDs are in-use.
479
480 ExitNow(tid = 0);
481 }
482 }
483
484 mCmdTidsInUse |= (1 << tid);
485 mCmdNextTid = SPINEL_GET_NEXT_TID(tid);
486
487 exit:
488 return tid;
489 }
490
FreeTidTableItem(spinel_tid_t aTid)491 void NcpSpinel::FreeTidTableItem(spinel_tid_t aTid)
492 {
493 mCmdTidsInUse &= ~(1 << aTid);
494
495 mCmdTable[aTid] = SPINEL_CMD_NOOP;
496 mWaitingKeyTable[aTid] = SPINEL_PROP_LAST_STATUS;
497 }
498
SetProperty(spinel_prop_key_t aKey,const EncodingFunc & aEncodingFunc)499 otError NcpSpinel::SetProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc)
500 {
501 otError error = OT_ERROR_NONE;
502 spinel_tid_t tid = GetNextTid();
503 uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid) | tid;
504
505 VerifyOrExit(tid != 0, error = OT_ERROR_BUSY);
506 SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_SET, aKey));
507 SuccessOrExit(error = aEncodingFunc());
508 SuccessOrExit(error = mEncoder.EndFrame());
509 SuccessOrExit(error = SendEncodedFrame());
510
511 mCmdTable[tid] = SPINEL_CMD_PROP_VALUE_SET;
512 mWaitingKeyTable[tid] = aKey;
513 exit:
514 if (error != OT_ERROR_NONE)
515 {
516 FreeTidTableItem(tid);
517 }
518 return error;
519 }
520
SendEncodedFrame(void)521 otError NcpSpinel::SendEncodedFrame(void)
522 {
523 otError error = OT_ERROR_NONE;
524 uint8_t frame[kTxBufferSize];
525 uint16_t frameLength;
526
527 SuccessOrExit(error = mNcpBuffer.OutFrameBegin());
528 frameLength = mNcpBuffer.OutFrameGetLength();
529 VerifyOrExit(mNcpBuffer.OutFrameRead(frameLength, frame) == frameLength, error = OT_ERROR_FAILED);
530 SuccessOrExit(error = mSpinelDriver->GetSpinelInterface()->SendFrame(frame, frameLength));
531
532 exit:
533 error = mNcpBuffer.OutFrameRemove();
534 return error;
535 }
536
ParseIp6AddressTable(const uint8_t * aBuf,uint16_t aLength,std::vector<Ip6AddressInfo> & aAddressTable)537 otError NcpSpinel::ParseIp6AddressTable(const uint8_t *aBuf,
538 uint16_t aLength,
539 std::vector<Ip6AddressInfo> &aAddressTable)
540 {
541 otError error = OT_ERROR_NONE;
542 ot::Spinel::Decoder decoder;
543
544 VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS);
545 decoder.Init(aBuf, aLength);
546
547 while (!decoder.IsAllReadInStruct())
548 {
549 Ip6AddressInfo cur;
550 const otIp6Address *addr;
551 uint8_t prefixLength;
552 uint32_t preferredLifetime;
553 uint32_t validLifetime;
554
555 SuccessOrExit(error = decoder.OpenStruct());
556 SuccessOrExit(error = decoder.ReadIp6Address(addr));
557 memcpy(&cur.mAddress, addr, sizeof(otIp6Address));
558 SuccessOrExit(error = decoder.ReadUint8(prefixLength));
559 cur.mPrefixLength = prefixLength;
560 SuccessOrExit(error = decoder.ReadUint32(preferredLifetime));
561 cur.mPreferred = preferredLifetime ? true : false;
562 SuccessOrExit(error = decoder.ReadUint32(validLifetime));
563 OTBR_UNUSED_VARIABLE(validLifetime);
564 SuccessOrExit((error = decoder.CloseStruct()));
565
566 aAddressTable.push_back(cur);
567 }
568
569 exit:
570 return error;
571 }
572
ParseIp6MulticastAddresses(const uint8_t * aBuf,uint8_t aLen,std::vector<Ip6Address> & aAddressList)573 otError NcpSpinel::ParseIp6MulticastAddresses(const uint8_t *aBuf, uint8_t aLen, std::vector<Ip6Address> &aAddressList)
574 {
575 otError error = OT_ERROR_NONE;
576 ot::Spinel::Decoder decoder;
577
578 VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS);
579
580 decoder.Init(aBuf, aLen);
581
582 while (!decoder.IsAllReadInStruct())
583 {
584 const otIp6Address *addr;
585
586 SuccessOrExit(error = decoder.OpenStruct());
587 SuccessOrExit(error = decoder.ReadIp6Address(addr));
588 aAddressList.emplace_back(Ip6Address(*addr));
589 SuccessOrExit((error = decoder.CloseStruct()));
590 }
591
592 exit:
593 return error;
594 }
595
ParseIp6StreamNet(const uint8_t * aBuf,uint8_t aLen,const uint8_t * & aData,uint16_t & aDataLen)596 otError NcpSpinel::ParseIp6StreamNet(const uint8_t *aBuf, uint8_t aLen, const uint8_t *&aData, uint16_t &aDataLen)
597 {
598 otError error = OT_ERROR_NONE;
599 ot::Spinel::Decoder decoder;
600
601 VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS);
602
603 decoder.Init(aBuf, aLen);
604 error = decoder.ReadDataWithLen(aData, aDataLen);
605
606 exit:
607 return error;
608 }
609
ParseOperationalDatasetTlvs(const uint8_t * aBuf,uint8_t aLen,otOperationalDatasetTlvs & aDatasetTlvs)610 otError NcpSpinel::ParseOperationalDatasetTlvs(const uint8_t *aBuf,
611 uint8_t aLen,
612 otOperationalDatasetTlvs &aDatasetTlvs)
613 {
614 otError error = OT_ERROR_NONE;
615 ot::Spinel::Decoder decoder;
616 const uint8_t *datasetTlvsData;
617 uint16_t datasetTlvsLen;
618
619 decoder.Init(aBuf, aLen);
620 SuccessOrExit(error = decoder.ReadData(datasetTlvsData, datasetTlvsLen));
621 VerifyOrExit(datasetTlvsLen <= sizeof(aDatasetTlvs.mTlvs), error = OT_ERROR_PARSE);
622
623 memcpy(aDatasetTlvs.mTlvs, datasetTlvsData, datasetTlvsLen);
624 aDatasetTlvs.mLength = datasetTlvsLen;
625
626 exit:
627 return error;
628 }
629
SpinelRoleToDeviceRole(spinel_net_role_t aRole)630 otDeviceRole NcpSpinel::SpinelRoleToDeviceRole(spinel_net_role_t aRole)
631 {
632 otDeviceRole role = OT_DEVICE_ROLE_DISABLED;
633
634 switch (aRole)
635 {
636 case SPINEL_NET_ROLE_DISABLED:
637 role = OT_DEVICE_ROLE_DISABLED;
638 break;
639 case SPINEL_NET_ROLE_DETACHED:
640 role = OT_DEVICE_ROLE_DETACHED;
641 break;
642 case SPINEL_NET_ROLE_CHILD:
643 role = OT_DEVICE_ROLE_CHILD;
644 break;
645 case SPINEL_NET_ROLE_ROUTER:
646 role = OT_DEVICE_ROLE_ROUTER;
647 break;
648 case SPINEL_NET_ROLE_LEADER:
649 role = OT_DEVICE_ROLE_LEADER;
650 break;
651 default:
652 otbrLogWarning("Unsupported spinel net role: %u", aRole);
653 break;
654 }
655
656 return role;
657 }
658
659 } // namespace Ncp
660 } // namespace otbr
661