1 /*
2  *  Copyright (c) 2023, 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 #include <openthread/config.h>
30 
31 #include "common/array.hpp"
32 #include "common/code_utils.hpp"
33 #include "instance/instance.hpp"
34 
35 #include "ncp/ncp_base.hpp"
36 #include "openthread/link_raw.h"
37 
38 #include "test_platform.h"
39 #include "test_util.hpp"
40 
41 using namespace ot;
42 using namespace ot::Ncp;
43 
44 enum
45 {
46     kTestBufferSize = 800
47 };
48 
49 enum
50 {
51     kTestMacScanChannelMask = 0x01
52 };
53 
54 OT_TOOL_PACKED_BEGIN
55 struct RadioMessage
56 {
57     uint8_t mChannel;
58     uint8_t mPsdu[OT_RADIO_FRAME_MAX_SIZE];
59 } OT_TOOL_PACKED_END;
60 
61 static struct RadioMessage sDefaultMessages[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM];
62 static otRadioFrame        sTxFrame[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM];
63 static ot::Instance       *sInstances[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM];
64 static ot::Instance       *sLastInstance;
65 
otPlatRadioGetTransmitBuffer(otInstance * aInstance)66 otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
67 {
68     otRadioFrame *frame = nullptr;
69 
70     for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++)
71     {
72         if (sInstances[i] == aInstance || sInstances[i] == nullptr)
73         {
74             sTxFrame[i].mPsdu = sDefaultMessages->mPsdu;
75             frame             = &sTxFrame[i];
76 
77             break;
78         }
79     }
80 
81     return frame;
82 }
83 
otPlatRadioTransmit(otInstance * aInstance,otRadioFrame *)84 otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *)
85 {
86     sLastInstance = static_cast<ot::Instance *>(aInstance);
87     return OT_ERROR_NONE;
88 }
89 
otPlatMultipanGetActiveInstance(otInstance ** aInstance)90 otError otPlatMultipanGetActiveInstance(otInstance **aInstance)
91 {
92     otError error = OT_ERROR_NOT_IMPLEMENTED;
93     OT_UNUSED_VARIABLE(aInstance);
94 
95 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
96     *aInstance = sLastInstance;
97     error      = OT_ERROR_NONE;
98 #endif
99 
100     return error;
101 }
102 
otPlatMultipanSetActiveInstance(otInstance * aInstance,bool aCompletePending)103 otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending)
104 {
105     otError error = OT_ERROR_NOT_IMPLEMENTED;
106     OT_UNUSED_VARIABLE(aInstance);
107     OT_UNUSED_VARIABLE(aCompletePending);
108 
109 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
110     VerifyOrExit(sLastInstance != static_cast<ot::Instance *>(aInstance), error = OT_ERROR_ALREADY);
111     sLastInstance = static_cast<ot::Instance *>(aInstance);
112     error         = OT_ERROR_NONE;
113 exit:
114 #endif
115 
116     return error;
117 }
118 
119 class TestNcp : public NcpBase
120 {
121 public:
TestNcp(ot::Instance * aInstance)122     explicit TestNcp(ot::Instance *aInstance)
123         : mLastHeader(0)
124         , mLastStatus(0)
125         , mLastProp(0)
126         , NcpBase(aInstance)
127     {
128         memset(mMsgBuffer, 0, kTestBufferSize);
129         mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
130         mTxFrameBuffer.SetFrameRemovedCallback(nullptr, this);
131     };
132 
TestNcp(ot::Instance ** aInstances,uint8_t aCount)133     explicit TestNcp(ot::Instance **aInstances, uint8_t aCount)
134         : mLastHeader(0)
135         , mLastStatus(0)
136         , mLastProp(0)
137         , NcpBase(aInstances, aCount)
138     {
139         memset(mMsgBuffer, 0, kTestBufferSize);
140         mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
141         mTxFrameBuffer.SetFrameRemovedCallback(nullptr, this);
142     };
143 
HandleFrameAddedToNcpBuffer(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aBuffer)144     static void HandleFrameAddedToNcpBuffer(void                    *aContext,
145                                             Spinel::Buffer::FrameTag aTag,
146                                             Spinel::Buffer::Priority aPriority,
147                                             Spinel::Buffer          *aBuffer)
148     {
149         OT_UNUSED_VARIABLE(aTag);
150         OT_UNUSED_VARIABLE(aPriority);
151 
152         static_cast<TestNcp *>(aContext)->HandleFrameAddedToNcpBuffer(aBuffer);
153     }
154 
HandleFrameAddedToNcpBuffer(Spinel::Buffer * aBuffer)155     void HandleFrameAddedToNcpBuffer(Spinel::Buffer *aBuffer)
156     {
157         static const size_t display_size = 64;
158 
159         memset(mMsgBuffer, 0, kTestBufferSize);
160         SuccessOrQuit(aBuffer->OutFrameBegin());
161         aBuffer->OutFrameRead(kTestBufferSize, mMsgBuffer);
162         SuccessOrQuit(aBuffer->OutFrameRemove());
163 
164         // DumpBuffer("Received Buffer", mMsgBuffer, display_size);
165 
166         updateSpinelStatus();
167     }
168 
Receive(uint8_t * aBuffer,size_t bufferSize)169     void Receive(uint8_t *aBuffer, size_t bufferSize) { HandleReceive(aBuffer, static_cast<uint16_t>(bufferSize)); }
170 
processTransmit()171     void processTransmit()
172     {
173         uint8_t iid = SPINEL_HEADER_GET_IID(mLastHeader);
174 
175         LinkRawTransmitDone(iid, &sTxFrame[iid], nullptr, OT_ERROR_NONE);
176     };
177 
updateSpinelStatus()178     void updateSpinelStatus()
179     {
180         Spinel::Decoder decoder;
181 
182         uint8_t      header;
183         unsigned int command;
184         unsigned int propKey;
185         unsigned int status;
186 
187         decoder.Init(mMsgBuffer, kTestBufferSize);
188 
189         SuccessOrQuit(decoder.ReadUint8(mLastHeader));
190         SuccessOrQuit(decoder.ReadUintPacked(command));
191         SuccessOrQuit(decoder.ReadUintPacked(propKey));
192         SuccessOrQuit(decoder.ReadUintPacked(status));
193 
194         mLastStatus = static_cast<uint32_t>(status);
195         mLastProp   = static_cast<uint32_t>(propKey);
196     }
197 
getSpinelStatus() const198     uint32_t getSpinelStatus() const { return mLastStatus; }
getSpinelProp() const199     uint32_t getSpinelProp() const { return mLastProp; }
200 
getLastIid() const201     uint8_t getLastIid() const
202     {
203         /* Return as SPINEL_HEADER_IID_N format without shift */
204         return SPINEL_HEADER_IID_MASK & mLastHeader;
205     }
206 
getLastTid()207     uint8_t getLastTid() { return SPINEL_HEADER_GET_TID(mLastHeader); }
208 
gotResponse(uint8_t aIid,uint8_t aTid)209     bool gotResponse(uint8_t aIid, uint8_t aTid) { return ((aIid == getLastIid()) && (aTid == getLastTid())); }
210 
211 private:
212     uint8_t  mLastHeader;
213     uint32_t mLastStatus;
214     uint32_t mLastProp;
215     uint8_t  mMsgBuffer[kTestBufferSize];
216 };
217 
218 class TestHost
219 {
220 public:
TestHost(TestNcp * aNcp,uint8_t aIid)221     TestHost(TestNcp *aNcp, uint8_t aIid)
222         : mNcp(aNcp)
223         , mIid(aIid)
224         , mTid(0)
225         , mLastTxTid(0)
226         , mBuffer(mBuf, kTestBufferSize)
227         , mEncoder(mBuffer)
228         , mOffset(0)
229     {
230         memset(mBuf, 0, kTestBufferSize);
231     };
232 
createLinkEnableFrame(bool isEnabled)233     void createLinkEnableFrame(bool isEnabled)
234     {
235         startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_ENABLED);
236         SuccessOrQuit(mEncoder.WriteBool(isEnabled));
237         endFrame("Enable Frame");
238     }
239 
createTransmitFrame()240     void createTransmitFrame()
241     {
242         startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW);
243 
244         SuccessOrQuit(mEncoder.WriteDataWithLen(sTxFrame[mIid].mPsdu, sTxFrame[mIid].mLength));
245         SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mChannel));
246         SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mInfo.mTxInfo.mMaxCsmaBackoffs));
247         SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mInfo.mTxInfo.mMaxFrameRetries));
248         SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mCsmaCaEnabled));
249         SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsHeaderUpdated));
250         SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsARetx));
251         SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsSecurityProcessed));
252         SuccessOrQuit(mEncoder.WriteUint32(sTxFrame[mIid].mInfo.mTxInfo.mTxDelay));
253         SuccessOrQuit(mEncoder.WriteUint32(sTxFrame[mIid].mInfo.mTxInfo.mTxDelayBaseTime));
254 
255         endFrame("Transmit Frame");
256     }
257 
createSwitchoverRequest(uint8_t aIid,bool aForce)258     void createSwitchoverRequest(uint8_t aIid, bool aForce)
259     {
260         startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE);
261         SuccessOrQuit(mEncoder.WriteUint8(aIid | (aForce ? 0 : (1 << SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT))));
262         endFrame("Interface Switch Request Frame");
263     }
264 
createReadStatusFrame()265     void createReadStatusFrame()
266     {
267         startFrame(SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_LAST_STATUS);
268         endFrame("Read Status Frame");
269     }
270 
enableRawLink()271     void enableRawLink()
272     {
273         static const bool isLinkEnabled = true;
274         createLinkEnableFrame(isLinkEnabled);
275         sendToRcp();
276     }
277 
disableRawLink()278     void disableRawLink()
279     {
280         static const bool isLinkEnabled = false;
281         createLinkEnableFrame(isLinkEnabled);
282         sendToRcp();
283     }
284 
startTransmit()285     spinel_status_t startTransmit()
286     {
287         mLastTxTid = mTid;
288         createTransmitFrame();
289         sendToRcp();
290         prepareResponse(mLastTxTid);
291         return static_cast<spinel_status_t>(mNcp->getSpinelStatus());
292     };
293 
requestSwitchover(uint8_t aIid,bool aForce)294     spinel_status_t requestSwitchover(uint8_t aIid, bool aForce)
295     {
296         mLastTxTid = mTid;
297         createSwitchoverRequest(aIid, aForce);
298         sendToRcp();
299         prepareResponse(mLastTxTid);
300         return static_cast<spinel_status_t>(mNcp->getSpinelStatus());
301     };
302 
getCommandStatus()303     void getCommandStatus()
304     {
305         createReadStatusFrame();
306         sendToRcp();
307     }
308 
finishTransmit()309     void finishTransmit()
310     {
311         /* Reset instance submac state to sleep by resetting link
312            This is needed for a second transmit command to succeed
313            as the HandleTimer method will not be called to reset the submac */
314         disableRawLink();
315         enableRawLink();
316 
317         /* Proceed with transmit done callback from ncp */
318         mNcp->processTransmit();
319     };
320 
getLastTransmitTid(void)321     uint8_t getLastTransmitTid(void) { return mLastTxTid; }
322 
323 private:
startFrame(unsigned int aCommand,spinel_prop_key_t aKey)324     void startFrame(unsigned int aCommand, spinel_prop_key_t aKey)
325     {
326         uint8_t spinelHeader = SPINEL_HEADER_FLAG | mIid | mTid;
327 
328         SuccessOrQuit(mEncoder.BeginFrame(Spinel::Buffer::kPriorityLow));
329         SuccessOrQuit(mEncoder.WriteUint8(spinelHeader));
330         SuccessOrQuit(mEncoder.WriteUintPacked(aCommand));
331         SuccessOrQuit(mEncoder.WriteUintPacked(aKey));
332     }
333 
endFrame(const char * aTextMessage)334     void endFrame(const char *aTextMessage)
335     {
336         static const uint16_t display_length = 64;
337         SuccessOrQuit(mEncoder.EndFrame());
338         // DumpBuffer(aTextMessage, mBuf, display_length);
339     }
340 
sendToRcp()341     void sendToRcp()
342     {
343         static const uint8_t data_offset = 2;
344         size_t               frame_len   = mBuffer.OutFrameGetLength();
345 
346         mOffset += data_offset;
347 
348         mNcp->Receive(mBuf + mOffset, frame_len);
349 
350         mTid = SPINEL_GET_NEXT_TID(mTid);
351         SuccessOrQuit(mBuffer.OutFrameRemove());
352 
353         mOffset += frame_len;
354         mOffset %= kTestBufferSize;
355     }
356 
prepareResponse(uint8_t aTid)357     void prepareResponse(uint8_t aTid)
358     {
359         /* Some spinel commands immediately send queued responses when command is complete
360         while others require a separate command to the ncp in order to receive the response.
361         If a response is needed and not immediately received. Issue a command to update the status. */
362 
363         if (!mNcp->gotResponse(mIid, aTid))
364         {
365             getCommandStatus();
366         }
367     }
368 
369     TestNcp        *mNcp;
370     uint8_t         mIid;
371     uint8_t         mTid;
372     uint8_t         mLastTxTid;
373     uint8_t         mBuf[kTestBufferSize];
374     Spinel::Buffer  mBuffer;
375     Spinel::Encoder mEncoder;
376     size_t          mOffset;
377 };
378 
InitInstances(void)379 void InitInstances(void)
380 {
381 #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE
382     for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++)
383     {
384         sInstances[i] = testInitAdditionalInstance(i);
385         VerifyOrQuit(sInstances[i] != nullptr);
386     }
387 #endif
388 }
389 
FreeInstances(void)390 void FreeInstances(void)
391 {
392     for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++)
393     {
394         if (sInstances[i] != nullptr)
395         {
396             testFreeInstance(sInstances[i]);
397             sInstances[i] = nullptr;
398         }
399     }
400 }
401 
TestNcpBaseTransmitWithLinkRawDisabled(void)402 void TestNcpBaseTransmitWithLinkRawDisabled(void)
403 {
404     printf("\tTransmit With Link Raw Disabled");
405     InitInstances();
406 
407     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
408     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
409     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
410     TestHost host3(&ncp, SPINEL_HEADER_IID_2);
411 
412     host1.disableRawLink();
413     host2.disableRawLink();
414     host3.disableRawLink();
415 
416     /* Test that the response status is Invalid State when transmit is skipped due to disabled link */
417     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_INVALID_STATE);
418     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_INVALID_STATE);
419     VerifyOrQuit(host3.startTransmit() == SPINEL_STATUS_INVALID_STATE);
420 
421     FreeInstances();
422     printf(" - PASS\n");
423 }
424 
TestNcpBaseTransmitWithLinkRawEnabled(void)425 void TestNcpBaseTransmitWithLinkRawEnabled(void)
426 {
427     printf("\tTransmit With Link Raw Enabled");
428     InitInstances();
429 
430     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
431     TestHost host(&ncp, SPINEL_HEADER_IID_0);
432 
433     host.enableRawLink();
434 
435     /* Test that the response status is OK when transmit is started successfully */
436     VerifyOrQuit(host.startTransmit() == SPINEL_STATUS_OK);
437 
438     host.finishTransmit();
439 
440     FreeInstances();
441     printf(" - PASS\n");
442 }
443 
TestNcpBaseTransmitWithIncorrectLinkRawEnabled(void)444 void TestNcpBaseTransmitWithIncorrectLinkRawEnabled(void)
445 {
446     printf("\tTransmit With Incorrect Link Raw Enabled");
447     InitInstances();
448 
449     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
450     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
451     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
452 
453     host1.disableRawLink();
454     host2.enableRawLink();
455 
456     /* Test that Invalid State is reported when different endpoint was enabled */
457     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_INVALID_STATE);
458 
459     /* Test that status is OK when transmitting on the proper interface */
460     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK);
461 
462     host1.finishTransmit();
463 
464     FreeInstances();
465     printf(" - PASS\n");
466 }
467 
TestNcpBaseTransmitOnBoth(void)468 void TestNcpBaseTransmitOnBoth(void)
469 {
470     printf("\tTransmit on both interfaces");
471     InitInstances();
472 
473     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
474     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
475     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
476 
477     host1.enableRawLink();
478     host2.enableRawLink();
479 
480     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK);
481     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK);
482 
483     host1.finishTransmit();
484     host2.finishTransmit();
485 
486     FreeInstances();
487     printf(" - PASS\n");
488 }
489 
TestNcpBaseDifferentInstanceCall(void)490 void TestNcpBaseDifferentInstanceCall(void)
491 {
492     printf("\tTransmit on both interfaces - verify instances used");
493 
494     InitInstances();
495 
496     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
497     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
498     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
499 
500     sLastInstance = nullptr;
501 
502     host1.enableRawLink();
503     host2.enableRawLink();
504 
505     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK);
506     VerifyOrQuit(sLastInstance != nullptr);
507     VerifyOrQuit(sLastInstance == sInstances[0]);
508 
509     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK);
510     VerifyOrQuit(sLastInstance != nullptr);
511     VerifyOrQuit(sLastInstance == sInstances[1]);
512 
513     host1.finishTransmit();
514     host2.finishTransmit();
515 
516     /* Test reverse order of calls to make sure it is not just a fixed order */
517     sLastInstance = nullptr;
518     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK);
519     VerifyOrQuit(sLastInstance != nullptr);
520     VerifyOrQuit(sLastInstance == sInstances[1]);
521 
522     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK);
523     VerifyOrQuit(sLastInstance != nullptr);
524     VerifyOrQuit(sLastInstance == sInstances[0]);
525 
526     host1.finishTransmit();
527     host2.finishTransmit();
528 
529     printf(" - PASS\n");
530 }
531 
TestNcpBaseTransmitDoneInterface(void)532 void TestNcpBaseTransmitDoneInterface(void)
533 {
534     printf("\tTransmit on both interfaces - verify transmit done IID");
535 
536     InitInstances();
537 
538     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
539     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
540     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
541 
542     sLastInstance = nullptr;
543 
544     host1.enableRawLink();
545     host2.enableRawLink();
546 
547     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK);
548     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK);
549 
550     otPlatRadioTxDone(sInstances[0], &sTxFrame[0], nullptr, OT_ERROR_NONE);
551     VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_0, host1.getLastTransmitTid()));
552 
553     otPlatRadioTxDone(sInstances[1], &sTxFrame[1], nullptr, OT_ERROR_NONE);
554     VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_1, host2.getLastTransmitTid()));
555 
556     /* Test reverse order of tx processing */
557     VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK);
558     VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK);
559 
560     otPlatRadioTxDone(sInstances[1], &sTxFrame[1], nullptr, OT_ERROR_NONE);
561     VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_1, host2.getLastTransmitTid()));
562 
563     otPlatRadioTxDone(sInstances[0], &sTxFrame[0], nullptr, OT_ERROR_NONE);
564     VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_0, host1.getLastTransmitTid()));
565 
566     printf(" - PASS\n");
567 }
568 
TestNcpBaseReceive(void)569 void TestNcpBaseReceive(void)
570 {
571     printf("\tReceive on a single interface");
572 
573     InitInstances();
574 
575     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
576     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
577 
578     host1.enableRawLink();
579 
580     otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE);
581 
582     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW);
583     VerifyOrQuit(ncp.getLastTid() == 0);
584     VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0);
585 
586     printf(" - PASS\n");
587 }
588 
TestNcpBaseReceiveOnTwoInterfaces(void)589 void TestNcpBaseReceiveOnTwoInterfaces(void)
590 {
591     printf("\tReceive on both interfaces");
592 
593     InitInstances();
594 
595     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
596     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
597     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
598 
599     sLastInstance = nullptr;
600 
601     host1.enableRawLink();
602     host2.enableRawLink();
603 
604     otPlatRadioReceiveDone(sInstances[1], &sTxFrame[1], OT_ERROR_NONE);
605 
606     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW);
607     VerifyOrQuit(ncp.getLastTid() == 0);
608     VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_1);
609 
610     otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE);
611 
612     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW);
613     VerifyOrQuit(ncp.getLastTid() == 0);
614     VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0);
615 
616     /* reverse order */
617     otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE);
618 
619     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW);
620     VerifyOrQuit(ncp.getLastTid() == 0);
621     VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0);
622 
623     otPlatRadioReceiveDone(sInstances[1], &sTxFrame[1], OT_ERROR_NONE);
624 
625     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW);
626     VerifyOrQuit(ncp.getLastTid() == 0);
627     VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_1);
628 
629     printf(" - PASS\n");
630 }
631 
TestNcpBaseSwitchoverRequest(void)632 void TestNcpBaseSwitchoverRequest(void)
633 {
634     printf("\tSwitchover requests from different interfaces");
635 
636     InitInstances();
637 
638     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
639     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
640     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
641     TestHost host3(&ncp, SPINEL_HEADER_IID_2);
642 
643     sLastInstance = nullptr;
644 
645     host1.enableRawLink();
646     host2.enableRawLink();
647     host3.enableRawLink();
648 
649     VerifyOrQuit(host1.requestSwitchover(0, true) == 0);
650     VerifyOrQuit(sLastInstance == sInstances[0]);
651     VerifyOrQuit(host1.requestSwitchover(1, true) == 1);
652     VerifyOrQuit(sLastInstance == sInstances[1]);
653     VerifyOrQuit(host1.requestSwitchover(2, true) == 2);
654     VerifyOrQuit(sLastInstance == sInstances[2]);
655     VerifyOrQuit(host2.requestSwitchover(0, true) == 0);
656     VerifyOrQuit(sLastInstance == sInstances[0]);
657     VerifyOrQuit(host2.requestSwitchover(1, true) == 1);
658     VerifyOrQuit(sLastInstance == sInstances[1]);
659     VerifyOrQuit(host2.requestSwitchover(2, true) == 2);
660     VerifyOrQuit(sLastInstance == sInstances[2]);
661     VerifyOrQuit(host3.requestSwitchover(0, true) == 0);
662     VerifyOrQuit(sLastInstance == sInstances[0]);
663     VerifyOrQuit(host3.requestSwitchover(1, true) == 1);
664     VerifyOrQuit(sLastInstance == sInstances[1]);
665     VerifyOrQuit(host3.requestSwitchover(2, true) == 2);
666     VerifyOrQuit(sLastInstance == sInstances[2]);
667 
668     printf(" - PASS\n");
669 }
670 
TestNcpBaseSwitchoverRequestFail(void)671 void TestNcpBaseSwitchoverRequestFail(void)
672 {
673     printf("\tSwitchover requests Fail - same interface");
674 
675     InitInstances();
676 
677     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
678     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
679     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
680 
681     sLastInstance = nullptr;
682 
683     host1.enableRawLink();
684     host2.enableRawLink();
685 
686     VerifyOrQuit(host1.requestSwitchover(0, true) == 0);
687     VerifyOrQuit(sLastInstance == sInstances[0]);
688 
689     VerifyOrQuit(host1.requestSwitchover(0, true) == SPINEL_STATUS_ALREADY);
690     VerifyOrQuit(sLastInstance == sInstances[0]);
691 
692     VerifyOrQuit(host2.requestSwitchover(0, true) == SPINEL_STATUS_ALREADY);
693     VerifyOrQuit(sLastInstance == sInstances[0]);
694 
695     printf(" - PASS\n");
696 }
697 
TestNcpBaseSwitchoverResponse(void)698 void TestNcpBaseSwitchoverResponse(void)
699 {
700     printf("\tSwitchover responses");
701 
702     InitInstances();
703 
704     TestNcp  ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM);
705     TestHost host1(&ncp, SPINEL_HEADER_IID_0);
706     TestHost host2(&ncp, SPINEL_HEADER_IID_1);
707 
708     sLastInstance = nullptr;
709 
710     host1.enableRawLink();
711     host2.enableRawLink();
712 
713     VerifyOrQuit(host1.requestSwitchover(0, true) == 0);
714     VerifyOrQuit(sLastInstance == sInstances[0]);
715 
716     otPlatMultipanSwitchoverDone(sLastInstance, true);
717 
718     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_LAST_STATUS);
719     VerifyOrQuit(ncp.getLastTid() == 0);
720     VerifyOrQuit(ncp.getLastIid() == OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID);
721     VerifyOrQuit(ncp.getSpinelStatus() == SPINEL_STATUS_SWITCHOVER_DONE);
722 
723     VerifyOrQuit(host1.requestSwitchover(1, true) == 1);
724     VerifyOrQuit(sLastInstance == sInstances[1]);
725 
726     otPlatMultipanSwitchoverDone(sLastInstance, false);
727 
728     VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_LAST_STATUS);
729     VerifyOrQuit(ncp.getLastTid() == 0);
730     VerifyOrQuit(ncp.getLastIid() == OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID);
731     VerifyOrQuit(ncp.getSpinelStatus() == SPINEL_STATUS_SWITCHOVER_FAILED);
732 
733     printf(" - PASS\n");
734 }
735 
736 ///
main(void)737 int main(void)
738 {
739 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && (OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE)
740     printf("Executing Transmit Tests\n");
741     TestNcpBaseTransmitWithLinkRawDisabled();
742     TestNcpBaseTransmitWithLinkRawEnabled();
743     TestNcpBaseTransmitWithIncorrectLinkRawEnabled();
744     TestNcpBaseTransmitOnBoth();
745     TestNcpBaseDifferentInstanceCall();
746     TestNcpBaseTransmitDoneInterface();
747     printf("Transmit Tests - PASS\n");
748 
749     printf("Executing Receive Tests\n");
750     TestNcpBaseReceive();
751     TestNcpBaseReceiveOnTwoInterfaces();
752     printf("Receive Tests - PASS\n");
753 
754     printf("Executing Interface Switching Tests\n");
755     TestNcpBaseSwitchoverRequest();
756     TestNcpBaseSwitchoverRequestFail();
757     TestNcpBaseSwitchoverResponse();
758     printf("Executing Interface Switching Tests - PASS\n");
759 
760     printf("\nAll tests passed\n");
761 
762 #else
763     printf("MULTIPAN_RCP feature and RADIO/LINK_RAW option are not enabled\n");
764 #endif
765     return 0;
766 }
767