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/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/log.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 mCallbacks.HandleFrameChangeDone(aChild);
125 }
126 }
127
HandleDataPoll(Mac::RxFrame & aFrame)128 void DataPollHandler::HandleDataPoll(Mac::RxFrame &aFrame)
129 {
130 Mac::Address macSource;
131 Child * child;
132 uint16_t indirectMsgCount;
133
134 VerifyOrExit(aFrame.GetSecurityEnabled());
135 VerifyOrExit(!Get<Mle::MleRouter>().IsDetached());
136
137 SuccessOrExit(aFrame.GetSrcAddr(macSource));
138 child = Get<ChildTable>().FindChild(macSource, Child::kInStateValidOrRestoring);
139 VerifyOrExit(child != nullptr);
140
141 child->SetLastHeard(TimerMilli::GetNow());
142 child->ResetLinkFailures();
143 #if OPENTHREAD_CONFIG_MULTI_RADIO
144 child->SetLastPollRadioType(aFrame.GetRadioType());
145 #endif
146
147 indirectMsgCount = child->GetIndirectMessageCount();
148
149 LogInfo("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
150 aFrame.GetRssi(), aFrame.IsAckedWithFramePending());
151
152 if (!aFrame.IsAckedWithFramePending())
153 {
154 if ((indirectMsgCount > 0) && macSource.IsShort())
155 {
156 Get<SourceMatchController>().SetSrcMatchAsShort(*child, true);
157 }
158
159 ExitNow();
160 }
161
162 if (mIndirectTxChild == nullptr)
163 {
164 mIndirectTxChild = child;
165 Get<Mac::Mac>().RequestIndirectFrameTransmission();
166 }
167 else
168 {
169 child->SetDataPollPending(true);
170 }
171
172 exit:
173 return;
174 }
175
HandleFrameRequest(Mac::TxFrames & aTxFrames)176 Mac::TxFrame *DataPollHandler::HandleFrameRequest(Mac::TxFrames &aTxFrames)
177 {
178 Mac::TxFrame *frame = nullptr;
179
180 VerifyOrExit(mIndirectTxChild != nullptr);
181
182 #if OPENTHREAD_CONFIG_MULTI_RADIO
183 frame = &aTxFrames.GetTxFrame(mIndirectTxChild->GetLastPollRadioType());
184 #else
185 frame = &aTxFrames.GetTxFrame();
186 #endif
187
188 VerifyOrExit(mCallbacks.PrepareFrameForChild(*frame, mFrameContext, *mIndirectTxChild) == kErrorNone,
189 frame = nullptr);
190
191 if (mIndirectTxChild->GetIndirectTxAttempts() > 0)
192 {
193 // For a re-transmission of an indirect frame to a sleepy
194 // child, we ensure to use the same frame counter, key id, and
195 // data sequence number as the previous attempt.
196
197 frame->SetIsARetransmission(true);
198 frame->SetSequence(mIndirectTxChild->GetIndirectDataSequenceNumber());
199
200 if (frame->GetSecurityEnabled())
201 {
202 frame->SetFrameCounter(mIndirectTxChild->GetIndirectFrameCounter());
203 frame->SetKeyId(mIndirectTxChild->GetIndirectKeyId());
204 }
205 }
206 else
207 {
208 frame->SetIsARetransmission(false);
209 }
210
211 exit:
212 return frame;
213 }
214
HandleSentFrame(const Mac::TxFrame & aFrame,Error aError)215 void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError)
216 {
217 Child *child = mIndirectTxChild;
218
219 VerifyOrExit(child != nullptr);
220
221 mIndirectTxChild = nullptr;
222 HandleSentFrame(aFrame, aError, *child);
223
224 exit:
225 ProcessPendingPolls();
226 }
227
HandleSentFrame(const Mac::TxFrame & aFrame,Error aError,Child & aChild)228 void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, Child &aChild)
229 {
230 if (aChild.IsFramePurgePending())
231 {
232 aChild.SetFramePurgePending(false);
233 aChild.SetFrameReplacePending(false);
234 aChild.ResetIndirectTxAttempts();
235 mCallbacks.HandleFrameChangeDone(aChild);
236 ExitNow();
237 }
238
239 switch (aError)
240 {
241 case kErrorNone:
242 aChild.ResetIndirectTxAttempts();
243 aChild.SetFrameReplacePending(false);
244 break;
245
246 case kErrorNoAck:
247 OT_ASSERT(!aFrame.GetSecurityEnabled() || aFrame.IsHeaderUpdated());
248
249 aChild.IncrementIndirectTxAttempts();
250 LogInfo("Indirect tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetIndirectTxAttempts(),
251 kMaxPollTriggeredTxAttempts);
252
253 OT_FALL_THROUGH;
254
255 case kErrorChannelAccessFailure:
256 case kErrorAbort:
257
258 if (aChild.IsFrameReplacePending())
259 {
260 aChild.SetFrameReplacePending(false);
261 aChild.ResetIndirectTxAttempts();
262 mCallbacks.HandleFrameChangeDone(aChild);
263 ExitNow();
264 }
265
266 if ((aChild.GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts) && !aFrame.IsEmpty())
267 {
268 // We save the frame counter, key id, and data sequence number of
269 // current frame so we use the same values for the retransmission
270 // of the frame following the receipt of the next data poll.
271
272 aChild.SetIndirectDataSequenceNumber(aFrame.GetSequence());
273
274 if (aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated())
275 {
276 uint32_t frameCounter;
277 uint8_t keyId;
278
279 SuccessOrAssert(aFrame.GetFrameCounter(frameCounter));
280 aChild.SetIndirectFrameCounter(frameCounter);
281
282 SuccessOrAssert(aFrame.GetKeyId(keyId));
283 aChild.SetIndirectKeyId(keyId);
284 }
285
286 ExitNow();
287 }
288
289 aChild.ResetIndirectTxAttempts();
290 break;
291
292 default:
293 OT_ASSERT(false);
294 OT_UNREACHABLE_CODE(break);
295 }
296
297 mCallbacks.HandleSentFrameToChild(aFrame, mFrameContext, aError, aChild);
298
299 exit:
300 return;
301 }
302
ProcessPendingPolls(void)303 void DataPollHandler::ProcessPendingPolls(void)
304 {
305 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
306 {
307 if (!child.IsDataPollPending())
308 {
309 continue;
310 }
311
312 // Find the child with earliest poll receive time.
313
314 if ((mIndirectTxChild == nullptr) || (child.GetLastHeard() < mIndirectTxChild->GetLastHeard()))
315 {
316 mIndirectTxChild = &child;
317 }
318 }
319
320 if (mIndirectTxChild != nullptr)
321 {
322 mIndirectTxChild->SetDataPollPending(false);
323 Get<Mac::Mac>().RequestIndirectFrameTransmission();
324 }
325 }
326
327 } // namespace ot
328
329 #endif // #if OPENTHREAD_FTD
330