1 /*
2 * Copyright (c) 2024, 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 "nexus_core.hpp"
30 #include "nexus_node.hpp"
31
32 namespace ot {
33 namespace Nexus {
34
35 Core *Core::sCore = nullptr;
36
Core(void)37 Core::Core(void)
38 : mNow(0)
39 , mCurNodeId(0)
40 , mPendingAction(false)
41 {
42 VerifyOrQuit(sCore == nullptr);
43 sCore = this;
44
45 mNextAlarmTime = mNow.GetDistantFuture();
46 }
47
~Core(void)48 Core::~Core(void) { sCore = nullptr; }
49
CreateNode(void)50 Node &Core::CreateNode(void)
51 {
52 Node *node;
53
54 node = Node::Allocate();
55 VerifyOrQuit(node != nullptr);
56
57 node->GetInstance().SetId(mCurNodeId++);
58
59 mNodes.Push(*node);
60
61 node->GetInstance().AfterInit();
62
63 return *node;
64 }
65
UpdateNextAlarmTime(const Alarm & aAlarm)66 void Core::UpdateNextAlarmTime(const Alarm &aAlarm)
67 {
68 if (aAlarm.mScheduled)
69 {
70 mNextAlarmTime = Min(mNextAlarmTime, Max(mNow, aAlarm.mAlarmTime));
71 }
72 }
73
AdvanceTime(uint32_t aDuration)74 void Core::AdvanceTime(uint32_t aDuration)
75 {
76 TimeMilli targetTime = mNow + aDuration;
77
78 while (mPendingAction || (mNextAlarmTime <= targetTime))
79 {
80 mNextAlarmTime = mNow.GetDistantFuture();
81 mPendingAction = false;
82
83 for (Node &node : mNodes)
84 {
85 Process(node);
86 UpdateNextAlarmTime(node.mAlarm);
87 }
88
89 if (!mPendingAction)
90 {
91 mNow = Min(mNextAlarmTime, targetTime);
92 }
93 }
94
95 mNow = targetTime;
96 }
97
Process(Node & aNode)98 void Core::Process(Node &aNode)
99 {
100 otTaskletsProcess(&aNode.GetInstance());
101
102 ProcessRadio(aNode);
103
104 if (aNode.mAlarm.ShouldTrigger(mNow))
105 {
106 otPlatAlarmMilliFired(&aNode.GetInstance());
107 }
108 }
109
ProcessRadio(Node & aNode)110 void Core::ProcessRadio(Node &aNode)
111 {
112 Mac::Address dstAddr;
113 uint16_t dstPanId;
114 bool ackRequested;
115 AckMode ackMode = kNoAck;
116
117 VerifyOrExit(aNode.mRadio.mState == Radio::kStateTransmit);
118
119 if (aNode.mRadio.mTxFrame.GetDstAddr(dstAddr) != kErrorNone)
120 {
121 dstAddr.SetNone();
122 }
123
124 if (aNode.mRadio.mTxFrame.GetDstPanId(dstPanId) != kErrorNone)
125 {
126 dstPanId = Mac::kPanIdBroadcast;
127 }
128
129 ackRequested = aNode.mRadio.mTxFrame.GetAckRequest();
130
131 otPlatRadioTxStarted(&aNode.GetInstance(), &aNode.mRadio.mTxFrame);
132
133 for (Node &rxNode : mNodes)
134 {
135 bool matchesDst;
136
137 if ((&rxNode == &aNode) || !rxNode.mRadio.CanReceiveOnChannel(aNode.mRadio.mTxFrame.GetChannel()))
138 {
139 continue;
140 }
141
142 matchesDst = rxNode.mRadio.Matches(dstAddr, dstPanId);
143
144 if (matchesDst || rxNode.mRadio.mPromiscuous)
145 {
146 // `rxNode` should receive this frame.
147
148 Radio::Frame rxFrame(aNode.mRadio.mTxFrame);
149
150 rxFrame.mInfo.mRxInfo.mTimestamp = (mNow.GetValue() * 1000u);
151 rxFrame.mInfo.mRxInfo.mRssi = kDefaultRxRssi;
152 rxFrame.mInfo.mRxInfo.mLqi = 0;
153
154 if (matchesDst && !dstAddr.IsNone() && !dstAddr.IsBroadcast() && ackRequested)
155 {
156 Mac::Address srcAddr;
157
158 ackMode = kSendAckNoFramePending;
159
160 if ((aNode.mRadio.mTxFrame.GetSrcAddr(srcAddr) == kErrorNone) &&
161 rxNode.mRadio.HasFramePendingFor(srcAddr))
162 {
163 ackMode = kSendAckFramePending;
164 rxFrame.mInfo.mRxInfo.mAckedWithFramePending = true;
165 }
166 }
167
168 otPlatRadioReceiveDone(&rxNode.GetInstance(), &rxFrame, kErrorNone);
169 }
170
171 if (ackMode != kNoAck)
172 {
173 // No need to go through rest of `mNodes`
174 // if already acked by a node.
175 break;
176 }
177 }
178
179 aNode.mRadio.mChannel = aNode.mRadio.mTxFrame.mChannel;
180 aNode.mRadio.mState = Radio::kStateReceive;
181
182 if (ackMode != kNoAck)
183 {
184 Mac::TxFrame ackFrame;
185 uint8_t ackPsdu[Mac::Frame::kImmAckLength];
186
187 ClearAllBytes(ackFrame);
188 ackFrame.mPsdu = ackPsdu;
189
190 ackFrame.GenerateImmAck(
191 static_cast<const Mac::RxFrame &>(static_cast<const Mac::Frame &>(aNode.mRadio.mTxFrame)),
192 (ackMode == kSendAckFramePending));
193
194 otPlatRadioTxDone(&aNode.GetInstance(), &aNode.mRadio.mTxFrame, &ackFrame, kErrorNone);
195 }
196 else
197 {
198 otPlatRadioTxDone(&aNode.GetInstance(), &aNode.mRadio.mTxFrame, nullptr,
199 ackRequested ? kErrorNoAck : kErrorNone);
200 }
201
202 exit:
203 return;
204 }
205
206 } // namespace Nexus
207 } // namespace ot
208