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