1 /*
2  *    Copyright (c) 2016-2017, 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" AND
17  *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20  *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /**
29  * @file
30  *   This file implements raw link required Spinel interface to the OpenThread stack.
31  */
32 
33 #include "ncp_base.hpp"
34 
35 #include <openthread/link.h>
36 #include <openthread/link_raw.h>
37 #include <openthread/ncp.h>
38 #include <openthread/platform/radio.h>
39 #include <openthread/platform/time.h>
40 
41 #include "common/code_utils.hpp"
42 #include "common/debug.hpp"
43 #include "common/instance.hpp"
44 #include "mac/mac_frame.hpp"
45 
46 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
47 
48 namespace ot {
49 namespace Ncp {
50 
51 #if OPENTHREAD_RADIO
HandlePropertyGet(void)52 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_API_VERSION>(void)
53 {
54     return mEncoder.WriteUintPacked(SPINEL_RCP_API_VERSION);
55 }
56 
HandlePropertyGet(void)57 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_MIN_HOST_API_VERSION>(void)
58 {
59     return mEncoder.WriteUintPacked(SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION);
60 }
61 #endif
62 
63 // ----------------------------------------------------------------------------
64 // MARK: Raw Link-Layer Datapath Glue
65 // ----------------------------------------------------------------------------
66 
PackRadioFrame(otRadioFrame * aFrame,otError aError)67 otError NcpBase::PackRadioFrame(otRadioFrame *aFrame, otError aError)
68 {
69     otError  error = OT_ERROR_FAILED;
70     uint16_t flags = 0;
71 
72     if (aFrame != nullptr && aError == OT_ERROR_NONE)
73     {
74         // Append the frame contents
75         SuccessOrExit(mEncoder.WriteDataWithLen(aFrame->mPsdu, aFrame->mLength));
76     }
77     else
78     {
79         // Append length
80         SuccessOrExit(mEncoder.WriteUint16(0));
81     }
82 
83     // Append metadata (rssi, etc)
84     SuccessOrExit(mEncoder.WriteInt8(aFrame ? aFrame->mInfo.mRxInfo.mRssi : 0)); // RSSI
85     SuccessOrExit(mEncoder.WriteInt8(-128));                                     // Noise Floor (Currently unused)
86 
87     if (aFrame != nullptr)
88     {
89         if (aFrame->mInfo.mRxInfo.mAckedWithFramePending)
90         {
91             flags |= SPINEL_MD_FLAG_ACKED_FP;
92         }
93 
94         if (aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
95         {
96             flags |= SPINEL_MD_FLAG_ACKED_SEC;
97         }
98     }
99 
100     SuccessOrExit(mEncoder.WriteUint16(flags)); // Flags
101 
102     SuccessOrExit(mEncoder.OpenStruct());                              // PHY-data
103     SuccessOrExit(mEncoder.WriteUint8(aFrame ? aFrame->mChannel : 0)); // 802.15.4 channel (Receive channel)
104     SuccessOrExit(mEncoder.WriteUint8(aFrame ? aFrame->mInfo.mRxInfo.mLqi
105                                              : static_cast<uint8_t>(OT_RADIO_LQI_NONE))); // 802.15.4 LQI
106 
107     SuccessOrExit(mEncoder.WriteUint64(aFrame ? aFrame->mInfo.mRxInfo.mTimestamp : 0)); // The timestamp in microseconds
108     SuccessOrExit(mEncoder.CloseStruct());
109 
110     SuccessOrExit(mEncoder.OpenStruct());            // Vendor-data
111     SuccessOrExit(mEncoder.WriteUintPacked(aError)); // Receive error
112     SuccessOrExit(mEncoder.CloseStruct());
113 
114     SuccessOrExit(mEncoder.OpenStruct());                                             // MAC-data
115     SuccessOrExit(mEncoder.WriteUint8(aFrame ? aFrame->mInfo.mRxInfo.mAckKeyId : 0)); // The ACK auxiliary key ID
116     SuccessOrExit(
117         mEncoder.WriteUint32(aFrame ? aFrame->mInfo.mRxInfo.mAckFrameCounter : 0)); // The ACK auxiliary frame counter
118     SuccessOrExit(mEncoder.CloseStruct());
119 
120     error = OT_ERROR_NONE;
121 
122 exit:
123     return error;
124 }
125 
LinkRawReceiveDone(otInstance *,otRadioFrame * aFrame,otError aError)126 void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aError)
127 {
128     sNcpInstance->LinkRawReceiveDone(aFrame, aError);
129 }
130 
LinkRawReceiveDone(otRadioFrame * aFrame,otError aError)131 void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError)
132 {
133     uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
134 
135     // Append frame header
136     SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW));
137 
138     SuccessOrExit(PackRadioFrame(aFrame, aError));
139     SuccessOrExit(mEncoder.EndFrame());
140 
141 exit:
142     return;
143 }
144 
LinkRawTransmitDone(otInstance *,otRadioFrame * aFrame,otRadioFrame * aAckFrame,otError aError)145 void NcpBase::LinkRawTransmitDone(otInstance *, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError)
146 {
147     sNcpInstance->LinkRawTransmitDone(aFrame, aAckFrame, aError);
148 }
149 
LinkRawTransmitDone(otRadioFrame * aFrame,otRadioFrame * aAckFrame,otError aError)150 void NcpBase::LinkRawTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError)
151 {
152     OT_UNUSED_VARIABLE(aFrame);
153 
154     if (mCurTransmitTID)
155     {
156         uint8_t header        = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mCurTransmitTID;
157         bool    framePending  = (aAckFrame != nullptr && static_cast<Mac::RxFrame *>(aAckFrame)->GetFramePending());
158         bool    headerUpdated = static_cast<Mac::TxFrame *>(aFrame)->IsHeaderUpdated();
159 
160         // Clear cached transmit TID
161         mCurTransmitTID = 0;
162 
163         SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_LAST_STATUS));
164         SuccessOrExit(mEncoder.WriteUintPacked(ThreadErrorToSpinelStatus(aError)));
165         SuccessOrExit(mEncoder.WriteBool(framePending));
166         SuccessOrExit(mEncoder.WriteBool(headerUpdated));
167 
168         if (aError == OT_ERROR_NONE)
169         {
170             SuccessOrExit(PackRadioFrame(aAckFrame, aError));
171         }
172 
173         if (static_cast<Mac::TxFrame *>(aFrame)->GetSecurityEnabled() && headerUpdated)
174         {
175             uint8_t  keyId;
176             uint32_t frameCounter;
177 
178             // Transmit frame auxiliary key index and frame counter
179             SuccessOrExit(static_cast<Mac::TxFrame *>(aFrame)->GetKeyId(keyId));
180             SuccessOrExit(static_cast<Mac::TxFrame *>(aFrame)->GetFrameCounter(frameCounter));
181 
182             SuccessOrExit(mEncoder.WriteUint8(keyId));
183             SuccessOrExit(mEncoder.WriteUint32(frameCounter));
184         }
185 
186         SuccessOrExit(mEncoder.EndFrame());
187     }
188 
189 exit:
190     return;
191 }
192 
LinkRawEnergyScanDone(otInstance *,int8_t aEnergyScanMaxRssi)193 void NcpBase::LinkRawEnergyScanDone(otInstance *, int8_t aEnergyScanMaxRssi)
194 {
195     sNcpInstance->LinkRawEnergyScanDone(aEnergyScanMaxRssi);
196 }
197 
LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi)198 void NcpBase::LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi)
199 {
200     int8_t scanChannel = mCurScanChannel;
201 
202     // Clear current scan channel
203     mCurScanChannel = kInvalidScanChannel;
204 
205     // Make sure we are back listening on the original receive channel,
206     // since the energy scan could have been on a different channel.
207     IgnoreError(otLinkRawReceive(mInstance));
208 
209     SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
210                                       SPINEL_PROP_MAC_ENERGY_SCAN_RESULT));
211 
212     SuccessOrExit(mEncoder.WriteUint8(static_cast<uint8_t>(scanChannel)));
213     SuccessOrExit(mEncoder.WriteInt8(aEnergyScanMaxRssi));
214     SuccessOrExit(mEncoder.EndFrame());
215 
216     // We are finished with the scan, so send out
217     // a property update indicating such.
218     SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
219                                       SPINEL_PROP_MAC_SCAN_STATE));
220 
221     SuccessOrExit(mEncoder.WriteUint8(SPINEL_SCAN_STATE_IDLE));
222     SuccessOrExit(mEncoder.EndFrame());
223 
224 exit:
225     return;
226 }
227 
HandlePropertyGet(void)228 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RADIO_CAPS>(void)
229 {
230     return mEncoder.WriteUintPacked(otLinkRawGetCaps(mInstance));
231 }
232 
HandlePropertyGet(void)233 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_SRC_MATCH_ENABLED>(void)
234 {
235     // TODO: Would be good to add an `otLinkRaw` API to give the status of source match.
236     return mEncoder.WriteBool(mSrcMatchEnabled);
237 }
238 
HandlePropertyGet(void)239 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_TIMESTAMP>(void)
240 {
241     otError error = OT_ERROR_NONE;
242 
243     SuccessOrExit(error = mEncoder.WriteUint64(otLinkRawGetRadioTime(mInstance)));
244 
245 exit:
246     return error;
247 }
248 
HandlePropertySet(void)249 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_SRC_MATCH_ENABLED>(void)
250 {
251     otError error = OT_ERROR_NONE;
252 
253     SuccessOrExit(error = mDecoder.ReadBool(mSrcMatchEnabled));
254 
255     error = otLinkRawSrcMatchEnable(mInstance, mSrcMatchEnabled);
256 
257 exit:
258     return error;
259 }
260 
HandlePropertySet(void)261 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES>(void)
262 {
263     otError error = OT_ERROR_NONE;
264 
265     // Clear the list first
266     SuccessOrExit(error = otLinkRawSrcMatchClearShortEntries(mInstance));
267 
268     // Loop through the addresses and add them
269     while (mDecoder.GetRemainingLengthInStruct() >= sizeof(uint16_t))
270     {
271         uint16_t shortAddress;
272 
273         SuccessOrExit(error = mDecoder.ReadUint16(shortAddress));
274 
275         SuccessOrExit(error = otLinkRawSrcMatchAddShortEntry(mInstance, shortAddress));
276     }
277 
278 exit:
279     return error;
280 }
281 
HandlePropertySet(void)282 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES>(void)
283 {
284     otError error = OT_ERROR_NONE;
285 
286     // Clear the list first
287     SuccessOrExit(error = otLinkRawSrcMatchClearExtEntries(mInstance));
288 
289     // Loop through the addresses and add them
290     while (mDecoder.GetRemainingLengthInStruct() >= sizeof(otExtAddress))
291     {
292         const otExtAddress *extAddress;
293 
294         SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
295 
296         SuccessOrExit(error = otLinkRawSrcMatchAddExtEntry(mInstance, extAddress));
297     }
298 
299 exit:
300     return error;
301 }
302 
HandlePropertyRemove(void)303 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES>(void)
304 {
305     otError  error = OT_ERROR_NONE;
306     uint16_t shortAddress;
307 
308     SuccessOrExit(error = mDecoder.ReadUint16(shortAddress));
309 
310     error = otLinkRawSrcMatchClearShortEntry(mInstance, shortAddress);
311 
312 exit:
313     return error;
314 }
315 
HandlePropertyRemove(void)316 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES>(void)
317 {
318     otError             error = OT_ERROR_NONE;
319     const otExtAddress *extAddress;
320 
321     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
322     ;
323 
324     error = otLinkRawSrcMatchClearExtEntry(mInstance, extAddress);
325 
326 exit:
327     return error;
328 }
329 
HandlePropertyInsert(void)330 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES>(void)
331 {
332     otError  error = OT_ERROR_NONE;
333     uint16_t shortAddress;
334 
335     SuccessOrExit(error = mDecoder.ReadUint16(shortAddress));
336 
337     error = otLinkRawSrcMatchAddShortEntry(mInstance, shortAddress);
338 
339 exit:
340     return error;
341 }
342 
HandlePropertyInsert(void)343 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES>(void)
344 {
345     otError             error      = OT_ERROR_NONE;
346     const otExtAddress *extAddress = nullptr;
347 
348     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
349 
350     error = otLinkRawSrcMatchAddExtEntry(mInstance, extAddress);
351 
352 exit:
353     return error;
354 }
355 
HandlePropertySet(void)356 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_ENABLED>(void)
357 {
358     bool    value = false;
359     otError error = OT_ERROR_NONE;
360 
361     SuccessOrExit(error = mDecoder.ReadBool(value));
362 
363     if (!value)
364     {
365         error = otLinkRawSetReceiveDone(mInstance, nullptr);
366     }
367     else
368     {
369         error = otLinkRawSetReceiveDone(mInstance, &NcpBase::LinkRawReceiveDone);
370     }
371 
372 exit:
373     return error;
374 }
375 
HandlePropertySet(void)376 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_15_4_SADDR>(void)
377 {
378     uint16_t shortAddress;
379     otError  error = OT_ERROR_NONE;
380 
381     SuccessOrExit(error = mDecoder.ReadUint16(shortAddress));
382 
383     error = otLinkRawSetShortAddress(mInstance, shortAddress);
384 
385 exit:
386     return error;
387 }
388 
DecodeStreamRawTxRequest(otRadioFrame & aFrame)389 otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame)
390 {
391     otError        error;
392     const uint8_t *payloadPtr;
393     uint16_t       payloadLen;
394     bool           csmaEnable;
395     bool           isARetx;
396     bool           isHeaderUpdated;
397     bool           isSecurityProcessed;
398 
399     SuccessOrExit(error = mDecoder.ReadDataWithLen(payloadPtr, payloadLen));
400     VerifyOrExit(payloadLen <= OT_RADIO_FRAME_MAX_SIZE, error = OT_ERROR_PARSE);
401 
402     aFrame.mLength = static_cast<uint8_t>(payloadLen);
403     memcpy(aFrame.mPsdu, payloadPtr, aFrame.mLength);
404 
405     // Parse the meta data
406 
407     // Channel is a required parameter in meta data.
408     SuccessOrExit(error = mDecoder.ReadUint8(aFrame.mChannel));
409 
410     // Set the default value for all optional parameters.
411     aFrame.mInfo.mTxInfo.mRxChannelAfterTxDone = aFrame.mChannel;
412     aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs      = OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT;
413     aFrame.mInfo.mTxInfo.mMaxFrameRetries      = OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT;
414     aFrame.mInfo.mTxInfo.mCsmaCaEnabled        = true;
415     aFrame.mInfo.mTxInfo.mIsHeaderUpdated      = false;
416     aFrame.mInfo.mTxInfo.mIsARetx              = false;
417     aFrame.mInfo.mTxInfo.mIsSecurityProcessed  = false;
418     aFrame.mInfo.mTxInfo.mTxDelay              = 0;
419     aFrame.mInfo.mTxInfo.mTxDelayBaseTime      = 0;
420 
421     // All the next parameters are optional. Note that even if the
422     // decoder fails to parse any of optional parameters we still want to
423     // return `OT_ERROR_NONE` (so `error` is not updated after this
424     // point).
425 
426     SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs));
427     SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mMaxFrameRetries));
428 
429     SuccessOrExit(mDecoder.ReadBool(csmaEnable));
430     aFrame.mInfo.mTxInfo.mCsmaCaEnabled = csmaEnable;
431 
432     SuccessOrExit(mDecoder.ReadBool(isHeaderUpdated));
433     aFrame.mInfo.mTxInfo.mIsHeaderUpdated = isHeaderUpdated;
434 
435     SuccessOrExit(mDecoder.ReadBool(isARetx));
436     aFrame.mInfo.mTxInfo.mIsARetx = isARetx;
437 
438     SuccessOrExit(mDecoder.ReadBool(isSecurityProcessed));
439     aFrame.mInfo.mTxInfo.mIsSecurityProcessed = isSecurityProcessed;
440 
441     SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelay));
442     SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelayBaseTime));
443     SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mRxChannelAfterTxDone));
444 
445 exit:
446     return error;
447 }
448 
HandlePropertySet_SPINEL_PROP_STREAM_RAW(uint8_t aHeader)449 otError NcpBase::HandlePropertySet_SPINEL_PROP_STREAM_RAW(uint8_t aHeader)
450 {
451     otError       error = OT_ERROR_NONE;
452     otRadioFrame *frame;
453 
454     VerifyOrExit(otLinkRawIsEnabled(mInstance), error = OT_ERROR_INVALID_STATE);
455 
456     frame = otLinkRawGetTransmitBuffer(mInstance);
457     VerifyOrExit(frame != nullptr, error = OT_ERROR_NO_BUFS);
458 
459     SuccessOrExit(error = DecodeStreamRawTxRequest(*frame));
460 
461     // Pass frame to the radio layer. Note, this fails if we
462     // haven't enabled raw stream or are already transmitting.
463     SuccessOrExit(error = otLinkRawTransmit(mInstance, &NcpBase::LinkRawTransmitDone));
464 
465     // Cache the transaction ID for async response
466     mCurTransmitTID = SPINEL_HEADER_GET_TID(aHeader);
467 
468 exit:
469 
470     if (error == OT_ERROR_NONE)
471     {
472         // Don't do anything here yet. We will complete the transaction when we get a transmit done callback
473     }
474     else
475     {
476         error = WriteLastStatusFrame(aHeader, ThreadErrorToSpinelStatus(error));
477     }
478 
479     return error;
480 }
481 
HandlePropertySet(void)482 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_RCP_MAC_KEY>(void)
483 {
484     otError        error = OT_ERROR_NONE;
485     uint8_t        keyIdMode;
486     uint8_t        keyId;
487     uint16_t       keySize;
488     const uint8_t *prevKey;
489     const uint8_t *currKey;
490     const uint8_t *nextKey;
491 
492     SuccessOrExit(error = mDecoder.ReadUint8(keyIdMode));
493     VerifyOrExit(keyIdMode == Mac::Frame::kKeyIdMode1, error = OT_ERROR_INVALID_ARGS);
494 
495     SuccessOrExit(error = mDecoder.ReadUint8(keyId));
496 
497     SuccessOrExit(error = mDecoder.ReadDataWithLen(prevKey, keySize));
498     VerifyOrExit(keySize == sizeof(otMacKey), error = OT_ERROR_INVALID_ARGS);
499 
500     SuccessOrExit(error = mDecoder.ReadDataWithLen(currKey, keySize));
501     VerifyOrExit(keySize == sizeof(otMacKey), error = OT_ERROR_INVALID_ARGS);
502 
503     SuccessOrExit(error = mDecoder.ReadDataWithLen(nextKey, keySize));
504     VerifyOrExit(keySize == sizeof(otMacKey), error = OT_ERROR_INVALID_ARGS);
505 
506     error =
507         otLinkRawSetMacKey(mInstance, keyIdMode, keyId, reinterpret_cast<const otMacKey *>(prevKey),
508                            reinterpret_cast<const otMacKey *>(currKey), reinterpret_cast<const otMacKey *>(nextKey));
509 
510 exit:
511     return error;
512 }
513 
HandlePropertySet(void)514 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_RCP_MAC_FRAME_COUNTER>(void)
515 {
516     otError  error = OT_ERROR_NONE;
517     uint32_t frameCounter;
518     bool     setIfLarger = false;
519 
520     SuccessOrExit(error = mDecoder.ReadUint32(frameCounter));
521 
522     if (!mDecoder.IsAllReadInStruct())
523     {
524         SuccessOrExit(error = mDecoder.ReadBool(setIfLarger));
525     }
526 
527     if (setIfLarger)
528     {
529         error = otLinkRawSetMacFrameCounterIfLarger(mInstance, frameCounter);
530     }
531     else
532     {
533         error = otLinkRawSetMacFrameCounter(mInstance, frameCounter);
534     }
535 
536 exit:
537     return error;
538 }
539 
540 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
HandlePropertySet(void)541 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_RCP_ENH_ACK_PROBING>(void)
542 {
543     otError             error = OT_ERROR_NONE;
544     uint16_t            shortAddress;
545     const otExtAddress *extAddress;
546     otLinkMetrics       linkMetrics = {false, false, false, false, false};
547 
548     SuccessOrExit(error = mDecoder.ReadUint16(shortAddress));
549     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
550     SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ true));
551 
552     error = otPlatRadioConfigureEnhAckProbing(mInstance, linkMetrics, shortAddress, extAddress);
553 
554 exit:
555     return error;
556 }
557 #endif
558 
559 } // namespace Ncp
560 } // namespace ot
561 
562 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
563