/* * Copyright (c) 2023, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "common/array.hpp" #include "common/code_utils.hpp" #include "instance/instance.hpp" #include "ncp/ncp_base.hpp" #include "openthread/link_raw.h" #include "test_platform.h" #include "test_util.hpp" using namespace ot; using namespace ot::Ncp; enum { kTestBufferSize = 800 }; enum { kTestMacScanChannelMask = 0x01 }; OT_TOOL_PACKED_BEGIN struct RadioMessage { uint8_t mChannel; uint8_t mPsdu[OT_RADIO_FRAME_MAX_SIZE]; } OT_TOOL_PACKED_END; static struct RadioMessage sDefaultMessages[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM]; static otRadioFrame sTxFrame[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM]; static ot::Instance *sInstances[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM]; static ot::Instance *sLastInstance; otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) { otRadioFrame *frame = nullptr; for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++) { if (sInstances[i] == aInstance || sInstances[i] == nullptr) { sTxFrame[i].mPsdu = sDefaultMessages->mPsdu; frame = &sTxFrame[i]; break; } } return frame; } otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *) { sLastInstance = static_cast(aInstance); return OT_ERROR_NONE; } otError otPlatMultipanGetActiveInstance(otInstance **aInstance) { otError error = OT_ERROR_NOT_IMPLEMENTED; OT_UNUSED_VARIABLE(aInstance); #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE *aInstance = sLastInstance; error = OT_ERROR_NONE; #endif return error; } otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending) { otError error = OT_ERROR_NOT_IMPLEMENTED; OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aCompletePending); #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE VerifyOrExit(sLastInstance != static_cast(aInstance), error = OT_ERROR_ALREADY); sLastInstance = static_cast(aInstance); error = OT_ERROR_NONE; exit: #endif return error; } class TestNcp : public NcpBase { public: explicit TestNcp(ot::Instance *aInstance) : mLastHeader(0) , mLastStatus(0) , mLastProp(0) , NcpBase(aInstance) { memset(mMsgBuffer, 0, kTestBufferSize); mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); mTxFrameBuffer.SetFrameRemovedCallback(nullptr, this); }; explicit TestNcp(ot::Instance **aInstances, uint8_t aCount) : mLastHeader(0) , mLastStatus(0) , mLastProp(0) , NcpBase(aInstances, aCount) { memset(mMsgBuffer, 0, kTestBufferSize); mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); mTxFrameBuffer.SetFrameRemovedCallback(nullptr, this); }; static void HandleFrameAddedToNcpBuffer(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, Spinel::Buffer *aBuffer) { OT_UNUSED_VARIABLE(aTag); OT_UNUSED_VARIABLE(aPriority); static_cast(aContext)->HandleFrameAddedToNcpBuffer(aBuffer); } void HandleFrameAddedToNcpBuffer(Spinel::Buffer *aBuffer) { static const size_t display_size = 64; memset(mMsgBuffer, 0, kTestBufferSize); SuccessOrQuit(aBuffer->OutFrameBegin()); aBuffer->OutFrameRead(kTestBufferSize, mMsgBuffer); SuccessOrQuit(aBuffer->OutFrameRemove()); // DumpBuffer("Received Buffer", mMsgBuffer, display_size); updateSpinelStatus(); } void Receive(uint8_t *aBuffer, size_t bufferSize) { HandleReceive(aBuffer, static_cast(bufferSize)); } void processTransmit() { uint8_t iid = SPINEL_HEADER_GET_IID(mLastHeader); LinkRawTransmitDone(iid, &sTxFrame[iid], nullptr, OT_ERROR_NONE); }; void updateSpinelStatus() { Spinel::Decoder decoder; uint8_t header; unsigned int command; unsigned int propKey; unsigned int status; decoder.Init(mMsgBuffer, kTestBufferSize); SuccessOrQuit(decoder.ReadUint8(mLastHeader)); SuccessOrQuit(decoder.ReadUintPacked(command)); SuccessOrQuit(decoder.ReadUintPacked(propKey)); SuccessOrQuit(decoder.ReadUintPacked(status)); mLastStatus = static_cast(status); mLastProp = static_cast(propKey); } uint32_t getSpinelStatus() const { return mLastStatus; } uint32_t getSpinelProp() const { return mLastProp; } uint8_t getLastIid() const { /* Return as SPINEL_HEADER_IID_N format without shift */ return SPINEL_HEADER_IID_MASK & mLastHeader; } uint8_t getLastTid() { return SPINEL_HEADER_GET_TID(mLastHeader); } bool gotResponse(uint8_t aIid, uint8_t aTid) { return ((aIid == getLastIid()) && (aTid == getLastTid())); } private: uint8_t mLastHeader; uint32_t mLastStatus; uint32_t mLastProp; uint8_t mMsgBuffer[kTestBufferSize]; }; class TestHost { public: TestHost(TestNcp *aNcp, uint8_t aIid) : mNcp(aNcp) , mIid(aIid) , mTid(0) , mLastTxTid(0) , mBuffer(mBuf, kTestBufferSize) , mEncoder(mBuffer) , mOffset(0) { memset(mBuf, 0, kTestBufferSize); }; void createLinkEnableFrame(bool isEnabled) { startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_ENABLED); SuccessOrQuit(mEncoder.WriteBool(isEnabled)); endFrame("Enable Frame"); } void createTransmitFrame() { startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW); SuccessOrQuit(mEncoder.WriteDataWithLen(sTxFrame[mIid].mPsdu, sTxFrame[mIid].mLength)); SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mChannel)); SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mInfo.mTxInfo.mMaxCsmaBackoffs)); SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mInfo.mTxInfo.mMaxFrameRetries)); SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mCsmaCaEnabled)); SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsHeaderUpdated)); SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsARetx)); SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsSecurityProcessed)); SuccessOrQuit(mEncoder.WriteUint32(sTxFrame[mIid].mInfo.mTxInfo.mTxDelay)); SuccessOrQuit(mEncoder.WriteUint32(sTxFrame[mIid].mInfo.mTxInfo.mTxDelayBaseTime)); endFrame("Transmit Frame"); } void createSwitchoverRequest(uint8_t aIid, bool aForce) { startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE); SuccessOrQuit(mEncoder.WriteUint8(aIid | (aForce ? 0 : (1 << SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT)))); endFrame("Interface Switch Request Frame"); } void createReadStatusFrame() { startFrame(SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_LAST_STATUS); endFrame("Read Status Frame"); } void enableRawLink() { static const bool isLinkEnabled = true; createLinkEnableFrame(isLinkEnabled); sendToRcp(); } void disableRawLink() { static const bool isLinkEnabled = false; createLinkEnableFrame(isLinkEnabled); sendToRcp(); } spinel_status_t startTransmit() { mLastTxTid = mTid; createTransmitFrame(); sendToRcp(); prepareResponse(mLastTxTid); return static_cast(mNcp->getSpinelStatus()); }; spinel_status_t requestSwitchover(uint8_t aIid, bool aForce) { mLastTxTid = mTid; createSwitchoverRequest(aIid, aForce); sendToRcp(); prepareResponse(mLastTxTid); return static_cast(mNcp->getSpinelStatus()); }; void getCommandStatus() { createReadStatusFrame(); sendToRcp(); } void finishTransmit() { /* Reset instance submac state to sleep by resetting link This is needed for a second transmit command to succeed as the HandleTimer method will not be called to reset the submac */ disableRawLink(); enableRawLink(); /* Proceed with transmit done callback from ncp */ mNcp->processTransmit(); }; uint8_t getLastTransmitTid(void) { return mLastTxTid; } private: void startFrame(unsigned int aCommand, spinel_prop_key_t aKey) { uint8_t spinelHeader = SPINEL_HEADER_FLAG | mIid | mTid; SuccessOrQuit(mEncoder.BeginFrame(Spinel::Buffer::kPriorityLow)); SuccessOrQuit(mEncoder.WriteUint8(spinelHeader)); SuccessOrQuit(mEncoder.WriteUintPacked(aCommand)); SuccessOrQuit(mEncoder.WriteUintPacked(aKey)); } void endFrame(const char *aTextMessage) { static const uint16_t display_length = 64; SuccessOrQuit(mEncoder.EndFrame()); // DumpBuffer(aTextMessage, mBuf, display_length); } void sendToRcp() { static const uint8_t data_offset = 2; size_t frame_len = mBuffer.OutFrameGetLength(); mOffset += data_offset; mNcp->Receive(mBuf + mOffset, frame_len); mTid = SPINEL_GET_NEXT_TID(mTid); SuccessOrQuit(mBuffer.OutFrameRemove()); mOffset += frame_len; mOffset %= kTestBufferSize; } void prepareResponse(uint8_t aTid) { /* Some spinel commands immediately send queued responses when command is complete while others require a separate command to the ncp in order to receive the response. If a response is needed and not immediately received. Issue a command to update the status. */ if (!mNcp->gotResponse(mIid, aTid)) { getCommandStatus(); } } TestNcp *mNcp; uint8_t mIid; uint8_t mTid; uint8_t mLastTxTid; uint8_t mBuf[kTestBufferSize]; Spinel::Buffer mBuffer; Spinel::Encoder mEncoder; size_t mOffset; }; void InitInstances(void) { #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++) { sInstances[i] = testInitAdditionalInstance(i); VerifyOrQuit(sInstances[i] != nullptr); } #endif } void FreeInstances(void) { for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++) { if (sInstances[i] != nullptr) { testFreeInstance(sInstances[i]); sInstances[i] = nullptr; } } } void TestNcpBaseTransmitWithLinkRawDisabled(void) { printf("\tTransmit With Link Raw Disabled"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); TestHost host3(&ncp, SPINEL_HEADER_IID_2); host1.disableRawLink(); host2.disableRawLink(); host3.disableRawLink(); /* Test that the response status is Invalid State when transmit is skipped due to disabled link */ VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_INVALID_STATE); VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_INVALID_STATE); VerifyOrQuit(host3.startTransmit() == SPINEL_STATUS_INVALID_STATE); FreeInstances(); printf(" - PASS\n"); } void TestNcpBaseTransmitWithLinkRawEnabled(void) { printf("\tTransmit With Link Raw Enabled"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host(&ncp, SPINEL_HEADER_IID_0); host.enableRawLink(); /* Test that the response status is OK when transmit is started successfully */ VerifyOrQuit(host.startTransmit() == SPINEL_STATUS_OK); host.finishTransmit(); FreeInstances(); printf(" - PASS\n"); } void TestNcpBaseTransmitWithIncorrectLinkRawEnabled(void) { printf("\tTransmit With Incorrect Link Raw Enabled"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); host1.disableRawLink(); host2.enableRawLink(); /* Test that Invalid State is reported when different endpoint was enabled */ VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_INVALID_STATE); /* Test that status is OK when transmitting on the proper interface */ VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); host1.finishTransmit(); FreeInstances(); printf(" - PASS\n"); } void TestNcpBaseTransmitOnBoth(void) { printf("\tTransmit on both interfaces"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); host1.enableRawLink(); host2.enableRawLink(); VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); host1.finishTransmit(); host2.finishTransmit(); FreeInstances(); printf(" - PASS\n"); } void TestNcpBaseDifferentInstanceCall(void) { printf("\tTransmit on both interfaces - verify instances used"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); sLastInstance = nullptr; host1.enableRawLink(); host2.enableRawLink(); VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(sLastInstance != nullptr); VerifyOrQuit(sLastInstance == sInstances[0]); VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(sLastInstance != nullptr); VerifyOrQuit(sLastInstance == sInstances[1]); host1.finishTransmit(); host2.finishTransmit(); /* Test reverse order of calls to make sure it is not just a fixed order */ sLastInstance = nullptr; VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(sLastInstance != nullptr); VerifyOrQuit(sLastInstance == sInstances[1]); VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(sLastInstance != nullptr); VerifyOrQuit(sLastInstance == sInstances[0]); host1.finishTransmit(); host2.finishTransmit(); printf(" - PASS\n"); } void TestNcpBaseTransmitDoneInterface(void) { printf("\tTransmit on both interfaces - verify transmit done IID"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); sLastInstance = nullptr; host1.enableRawLink(); host2.enableRawLink(); VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); otPlatRadioTxDone(sInstances[0], &sTxFrame[0], nullptr, OT_ERROR_NONE); VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_0, host1.getLastTransmitTid())); otPlatRadioTxDone(sInstances[1], &sTxFrame[1], nullptr, OT_ERROR_NONE); VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_1, host2.getLastTransmitTid())); /* Test reverse order of tx processing */ VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); otPlatRadioTxDone(sInstances[1], &sTxFrame[1], nullptr, OT_ERROR_NONE); VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_1, host2.getLastTransmitTid())); otPlatRadioTxDone(sInstances[0], &sTxFrame[0], nullptr, OT_ERROR_NONE); VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_0, host1.getLastTransmitTid())); printf(" - PASS\n"); } void TestNcpBaseReceive(void) { printf("\tReceive on a single interface"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); host1.enableRawLink(); otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0); printf(" - PASS\n"); } void TestNcpBaseReceiveOnTwoInterfaces(void) { printf("\tReceive on both interfaces"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); sLastInstance = nullptr; host1.enableRawLink(); host2.enableRawLink(); otPlatRadioReceiveDone(sInstances[1], &sTxFrame[1], OT_ERROR_NONE); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_1); otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0); /* reverse order */ otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0); otPlatRadioReceiveDone(sInstances[1], &sTxFrame[1], OT_ERROR_NONE); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_1); printf(" - PASS\n"); } void TestNcpBaseSwitchoverRequest(void) { printf("\tSwitchover requests from different interfaces"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); TestHost host3(&ncp, SPINEL_HEADER_IID_2); sLastInstance = nullptr; host1.enableRawLink(); host2.enableRawLink(); host3.enableRawLink(); VerifyOrQuit(host1.requestSwitchover(0, true) == 0); VerifyOrQuit(sLastInstance == sInstances[0]); VerifyOrQuit(host1.requestSwitchover(1, true) == 1); VerifyOrQuit(sLastInstance == sInstances[1]); VerifyOrQuit(host1.requestSwitchover(2, true) == 2); VerifyOrQuit(sLastInstance == sInstances[2]); VerifyOrQuit(host2.requestSwitchover(0, true) == 0); VerifyOrQuit(sLastInstance == sInstances[0]); VerifyOrQuit(host2.requestSwitchover(1, true) == 1); VerifyOrQuit(sLastInstance == sInstances[1]); VerifyOrQuit(host2.requestSwitchover(2, true) == 2); VerifyOrQuit(sLastInstance == sInstances[2]); VerifyOrQuit(host3.requestSwitchover(0, true) == 0); VerifyOrQuit(sLastInstance == sInstances[0]); VerifyOrQuit(host3.requestSwitchover(1, true) == 1); VerifyOrQuit(sLastInstance == sInstances[1]); VerifyOrQuit(host3.requestSwitchover(2, true) == 2); VerifyOrQuit(sLastInstance == sInstances[2]); printf(" - PASS\n"); } void TestNcpBaseSwitchoverRequestFail(void) { printf("\tSwitchover requests Fail - same interface"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); sLastInstance = nullptr; host1.enableRawLink(); host2.enableRawLink(); VerifyOrQuit(host1.requestSwitchover(0, true) == 0); VerifyOrQuit(sLastInstance == sInstances[0]); VerifyOrQuit(host1.requestSwitchover(0, true) == SPINEL_STATUS_ALREADY); VerifyOrQuit(sLastInstance == sInstances[0]); VerifyOrQuit(host2.requestSwitchover(0, true) == SPINEL_STATUS_ALREADY); VerifyOrQuit(sLastInstance == sInstances[0]); printf(" - PASS\n"); } void TestNcpBaseSwitchoverResponse(void) { printf("\tSwitchover responses"); InitInstances(); TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); TestHost host1(&ncp, SPINEL_HEADER_IID_0); TestHost host2(&ncp, SPINEL_HEADER_IID_1); sLastInstance = nullptr; host1.enableRawLink(); host2.enableRawLink(); VerifyOrQuit(host1.requestSwitchover(0, true) == 0); VerifyOrQuit(sLastInstance == sInstances[0]); otPlatMultipanSwitchoverDone(sLastInstance, true); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_LAST_STATUS); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID); VerifyOrQuit(ncp.getSpinelStatus() == SPINEL_STATUS_SWITCHOVER_DONE); VerifyOrQuit(host1.requestSwitchover(1, true) == 1); VerifyOrQuit(sLastInstance == sInstances[1]); otPlatMultipanSwitchoverDone(sLastInstance, false); VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_LAST_STATUS); VerifyOrQuit(ncp.getLastTid() == 0); VerifyOrQuit(ncp.getLastIid() == OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID); VerifyOrQuit(ncp.getSpinelStatus() == SPINEL_STATUS_SWITCHOVER_FAILED); printf(" - PASS\n"); } /// int main(void) { #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && (OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE) printf("Executing Transmit Tests\n"); TestNcpBaseTransmitWithLinkRawDisabled(); TestNcpBaseTransmitWithLinkRawEnabled(); TestNcpBaseTransmitWithIncorrectLinkRawEnabled(); TestNcpBaseTransmitOnBoth(); TestNcpBaseDifferentInstanceCall(); TestNcpBaseTransmitDoneInterface(); printf("Transmit Tests - PASS\n"); printf("Executing Receive Tests\n"); TestNcpBaseReceive(); TestNcpBaseReceiveOnTwoInterfaces(); printf("Receive Tests - PASS\n"); printf("Executing Interface Switching Tests\n"); TestNcpBaseSwitchoverRequest(); TestNcpBaseSwitchoverRequestFail(); TestNcpBaseSwitchoverResponse(); printf("Executing Interface Switching Tests - PASS\n"); printf("\nAll tests passed\n"); #else printf("MULTIPAN_RCP feature and RADIO/LINK_RAW option are not enabled\n"); #endif return 0; }