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