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