xref: /aosp_15_r20/external/ot-br-posix/src/ncp/ncp_spinel.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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