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