1 /*
2 * Copyright (c) 2019, 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 /**
30 * @file
31 * This file includes the implementation for handling of data polls and indirect frame transmission.
32 */
33
34 #include "data_poll_handler.hpp"
35
36 #if OPENTHREAD_FTD
37
38 #include "instance/instance.hpp"
39
40 namespace ot {
41
42 RegisterLogModule("DataPollHandlr");
43
DataPollHandler(Instance & aInstance)44 DataPollHandler::DataPollHandler(Instance &aInstance)
45 : InstanceLocator(aInstance)
46 , mIndirectTxChild(nullptr)
47 , mFrameContext()
48 {
49 }
50
Clear(void)51 void DataPollHandler::Clear(void)
52 {
53 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
54 {
55 child.SetDataPollPending(false);
56 child.SetFrameReplacePending(false);
57 child.SetFramePurgePending(false);
58 child.ResetIndirectTxAttempts();
59 }
60
61 mIndirectTxChild = nullptr;
62 }
63
RequestFrameChange(FrameChange aChange,Child & aChild)64 void DataPollHandler::RequestFrameChange(FrameChange aChange, Child &aChild)
65 {
66 if ((mIndirectTxChild == &aChild) && Get<Mac::Mac>().IsPerformingIndirectTransmit())
67 {
68 switch (aChange)
69 {
70 case kReplaceFrame:
71 aChild.SetFrameReplacePending(true);
72 break;
73
74 case kPurgeFrame:
75 aChild.SetFramePurgePending(true);
76 break;
77 }
78 }
79 else
80 {
81 ResetTxAttempts(aChild);
82 Get<IndirectSender>().HandleFrameChangeDone(aChild);
83 }
84 }
85
HandleDataPoll(Mac::RxFrame & aFrame)86 void DataPollHandler::HandleDataPoll(Mac::RxFrame &aFrame)
87 {
88 Mac::Address macSource;
89 Child *child;
90 uint16_t indirectMsgCount;
91
92 VerifyOrExit(aFrame.GetSecurityEnabled());
93 VerifyOrExit(!Get<Mle::MleRouter>().IsDetached());
94
95 SuccessOrExit(aFrame.GetSrcAddr(macSource));
96 child = Get<ChildTable>().FindChild(macSource, Child::kInStateValidOrRestoring);
97 VerifyOrExit(child != nullptr);
98
99 child->SetLastHeard(TimerMilli::GetNow());
100 child->ResetLinkFailures();
101 #if OPENTHREAD_CONFIG_MULTI_RADIO
102 child->SetLastPollRadioType(aFrame.GetRadioType());
103 #endif
104
105 indirectMsgCount = child->GetIndirectMessageCount();
106
107 LogInfo("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
108 aFrame.GetRssi(), aFrame.IsAckedWithFramePending());
109
110 if (!aFrame.IsAckedWithFramePending())
111 {
112 if ((indirectMsgCount > 0) && macSource.IsShort())
113 {
114 Get<SourceMatchController>().SetSrcMatchAsShort(*child, true);
115 }
116
117 ExitNow();
118 }
119
120 if (mIndirectTxChild == nullptr)
121 {
122 mIndirectTxChild = child;
123 Get<Mac::Mac>().RequestIndirectFrameTransmission();
124 }
125 else
126 {
127 child->SetDataPollPending(true);
128 }
129
130 exit:
131 return;
132 }
133
HandleFrameRequest(Mac::TxFrames & aTxFrames)134 Mac::TxFrame *DataPollHandler::HandleFrameRequest(Mac::TxFrames &aTxFrames)
135 {
136 Mac::TxFrame *frame = nullptr;
137
138 VerifyOrExit(mIndirectTxChild != nullptr);
139
140 #if OPENTHREAD_CONFIG_MULTI_RADIO
141 frame = &aTxFrames.GetTxFrame(mIndirectTxChild->GetLastPollRadioType());
142 #else
143 frame = &aTxFrames.GetTxFrame();
144 #endif
145
146 VerifyOrExit(Get<IndirectSender>().PrepareFrameForChild(*frame, mFrameContext, *mIndirectTxChild) == kErrorNone,
147 frame = nullptr);
148
149 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
150 if ((mIndirectTxChild->GetIndirectTxAttempts() > 0) || (mIndirectTxChild->GetCslTxAttempts() > 0))
151 #else
152 if (mIndirectTxChild->GetIndirectTxAttempts() > 0)
153 #endif
154 {
155 // For a re-transmission of an indirect frame to a sleepy
156 // child, we ensure to use the same frame counter, key id, and
157 // data sequence number as the previous attempt.
158
159 frame->SetIsARetransmission(true);
160 frame->SetSequence(mIndirectTxChild->GetIndirectDataSequenceNumber());
161
162 if (frame->GetSecurityEnabled())
163 {
164 frame->SetFrameCounter(mIndirectTxChild->GetIndirectFrameCounter());
165 frame->SetKeyId(mIndirectTxChild->GetIndirectKeyId());
166 }
167 }
168 else
169 {
170 frame->SetIsARetransmission(false);
171 }
172
173 exit:
174 return frame;
175 }
176
HandleSentFrame(const Mac::TxFrame & aFrame,Error aError)177 void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError)
178 {
179 Child *child = mIndirectTxChild;
180
181 VerifyOrExit(child != nullptr);
182
183 mIndirectTxChild = nullptr;
184 HandleSentFrame(aFrame, aError, *child);
185
186 exit:
187 ProcessPendingPolls();
188 }
189
HandleSentFrame(const Mac::TxFrame & aFrame,Error aError,Child & aChild)190 void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, Child &aChild)
191 {
192 if (aChild.IsFramePurgePending())
193 {
194 aChild.SetFramePurgePending(false);
195 aChild.SetFrameReplacePending(false);
196 ResetTxAttempts(aChild);
197 Get<IndirectSender>().HandleFrameChangeDone(aChild);
198 ExitNow();
199 }
200
201 switch (aError)
202 {
203 case kErrorNone:
204 ResetTxAttempts(aChild);
205 aChild.SetFrameReplacePending(false);
206 break;
207
208 case kErrorNoAck:
209 OT_ASSERT(!aFrame.GetSecurityEnabled() || aFrame.IsHeaderUpdated());
210
211 aChild.IncrementIndirectTxAttempts();
212 LogInfo("Indirect tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetIndirectTxAttempts(),
213 kMaxPollTriggeredTxAttempts);
214
215 OT_FALL_THROUGH;
216
217 case kErrorChannelAccessFailure:
218 case kErrorAbort:
219
220 if (aChild.IsFrameReplacePending())
221 {
222 aChild.SetFrameReplacePending(false);
223 ResetTxAttempts(aChild);
224 Get<IndirectSender>().HandleFrameChangeDone(aChild);
225 ExitNow();
226 }
227
228 if ((aChild.GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts) && !aFrame.IsEmpty())
229 {
230 // We save the frame counter, key id, and data sequence number of
231 // current frame so we use the same values for the retransmission
232 // of the frame following the receipt of the next data poll.
233
234 aChild.SetIndirectDataSequenceNumber(aFrame.GetSequence());
235
236 if (aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated())
237 {
238 uint32_t frameCounter;
239 uint8_t keyId;
240
241 SuccessOrAssert(aFrame.GetFrameCounter(frameCounter));
242 aChild.SetIndirectFrameCounter(frameCounter);
243
244 SuccessOrAssert(aFrame.GetKeyId(keyId));
245 aChild.SetIndirectKeyId(keyId);
246 }
247
248 ExitNow();
249 }
250
251 aChild.ResetIndirectTxAttempts();
252 break;
253
254 default:
255 OT_ASSERT(false);
256 }
257
258 Get<IndirectSender>().HandleSentFrameToChild(aFrame, mFrameContext, aError, aChild);
259
260 exit:
261 return;
262 }
263
ProcessPendingPolls(void)264 void DataPollHandler::ProcessPendingPolls(void)
265 {
266 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
267 {
268 if (!child.IsDataPollPending())
269 {
270 continue;
271 }
272
273 // Find the child with earliest poll receive time.
274
275 if ((mIndirectTxChild == nullptr) || (child.GetLastHeard() < mIndirectTxChild->GetLastHeard()))
276 {
277 mIndirectTxChild = &child;
278 }
279 }
280
281 if (mIndirectTxChild != nullptr)
282 {
283 mIndirectTxChild->SetDataPollPending(false);
284 Get<Mac::Mac>().RequestIndirectFrameTransmission();
285 }
286 }
287
ResetTxAttempts(Child & aChild)288 void DataPollHandler::ResetTxAttempts(Child &aChild)
289 {
290 aChild.ResetIndirectTxAttempts();
291
292 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
293 aChild.ResetCslTxAttempts();
294 #endif
295 }
296
297 } // namespace ot
298
299 #endif // #if OPENTHREAD_FTD
300