1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/l2cap/enhanced_retransmission_mode_engines.h"
16
17 #include <gmock/gmock.h>
18 #include <pw_async/fake_dispatcher_fixture.h>
19
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_tx_channel.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/fragmenter.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
23 #include "pw_unit_test/framework.h"
24
25 namespace bt::l2cap::internal {
26 namespace {
27
28 class EnhancedRetransmissionModeEnginesTest
29 : public pw::async::test::FakeDispatcherFixture {
30 protected:
TxProcessSdu(TxEngine & engine,ByteBufferPtr sdu)31 void TxProcessSdu(TxEngine& engine, ByteBufferPtr sdu) {
32 channel_.QueueSdu(std::move(sdu));
33 engine.NotifySduQueued();
34 }
35
channel()36 FakeTxChannel& channel() { return channel_; }
37
38 private:
39 FakeTxChannel channel_;
40 };
41
42 constexpr size_t kMaxTransmissions = 2;
43 constexpr size_t kTxWindow = 63;
44
45 constexpr hci_spec::ConnectionHandle kTestHandle = 0x0001;
46 constexpr ChannelId kTestChannelId = 0x0001;
47 constexpr uint8_t kExtendedControlFBitMask = 0b1000'0000;
48 constexpr uint8_t kExtendedControlPBitMask = 0b0001'0000;
49 constexpr uint8_t kExtendedControlRejFunctionMask = 0b0000'0100;
50 constexpr uint8_t kExtendedControlReceiverNotReadyFunctionMask = 0b0000'1000;
51 constexpr uint8_t kExtendedControlSrejFunctionMask = 0b0000'1100;
52
NoOpFailureCallback()53 void NoOpFailureCallback() {}
54
TEST_F(EnhancedRetransmissionModeEnginesTest,MakeLinkedERTMEngines)55 TEST_F(EnhancedRetransmissionModeEnginesTest, MakeLinkedERTMEngines) {
56 auto [rx_engine, tx_engine] =
57 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
58 kDefaultMTU,
59 kMaxTransmissions,
60 kTxWindow,
61 channel(),
62 NoOpFailureCallback,
63 dispatcher());
64 EXPECT_TRUE(rx_engine);
65 EXPECT_TRUE(tx_engine);
66 }
67
68 // This test that TxEngine sends I-Frames whose acknowledgement sequence numbers
69 // match the receive sequence number in RxEngine. This also tests that peer
70 // acknowledgement restarts our outbound data that was paused by hitting
71 // TxWindow. This mirrors the L2CAP Test Specification L2CAP/ERM/BV-06-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,OutboundInformationFramesAcknowledgeReceivedInformationFrames)72 TEST_F(EnhancedRetransmissionModeEnginesTest,
73 OutboundInformationFramesAcknowledgeReceivedInformationFrames) {
74 int tx_count = 0;
75 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
76 ASSERT_TRUE(pdu);
77 // Unlike the Test Spec's sequence diagram, we respond to the peer's
78 // I-Frame with a Receiver Ready (which is always allowed), so our
79 // subsequent I-Frame is actually the third outbound.
80 if (tx_count == 0) {
81 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
82 ASSERT_TRUE(
83 pdu->To<EnhancedControlField>().designates_information_frame());
84 const auto& header = pdu->To<SimpleInformationFrameHeader>();
85 EXPECT_EQ(0, header.tx_seq());
86 EXPECT_EQ(0, header.receive_seq_num());
87 } else if (tx_count == 2) {
88 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
89 ASSERT_TRUE(
90 pdu->To<EnhancedControlField>().designates_information_frame());
91 const auto& header = pdu->To<SimpleInformationFrameHeader>();
92 EXPECT_EQ(1, header.tx_seq());
93
94 // Acknowledges the I-Frame from the peer.
95 EXPECT_EQ(1, header.receive_seq_num());
96 }
97 tx_count++;
98 });
99 auto [rx_engine, tx_engine] =
100 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
101 kDefaultMTU,
102 kMaxTransmissions,
103 /*n_frames_in_tx_window=*/1,
104 channel(),
105 NoOpFailureCallback,
106 dispatcher());
107 ASSERT_TRUE(rx_engine);
108 ASSERT_TRUE(tx_engine);
109
110 TxProcessSdu(*tx_engine,
111 std::make_unique<DynamicByteBuffer>(
112 StaticByteBuffer{'p', 'i', 'n', 'g'}));
113 EXPECT_EQ(1, tx_count);
114
115 // Receive an I-frame containing an acknowledgment including the frame that we
116 // transmitted. See Core Spec, v5, Vol 3, Part A, Section 3.3.2, I-Frame
117 // Enhanced Control Field.
118 StaticByteBuffer info_frame(0, 1, 'p', 'o', 'n', 'g');
119 EXPECT_TRUE(rx_engine->ProcessPdu(
120 Fragmenter(kTestHandle)
121 .BuildFrame(kTestChannelId,
122 info_frame,
123 FrameCheckSequenceOption::kIncludeFcs)));
124 EXPECT_EQ(2, tx_count);
125
126 TxProcessSdu(*tx_engine,
127 std::make_unique<DynamicByteBuffer>(
128 StaticByteBuffer{'b', 'y', 'e', 'e'}));
129 EXPECT_EQ(3, tx_count);
130 }
131
132 // This test simulates a peer's non-response to our S-Frame poll request which
133 // causes us to raise a channel error after the monitor timer expires. This
134 // mirrors L2CAP Test Specification v5.0.2 L2CAP/ERM/BV-11-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,SignalFailureAfterMonitorTimerExpiry)135 TEST_F(EnhancedRetransmissionModeEnginesTest,
136 SignalFailureAfterMonitorTimerExpiry) {
137 int tx_count = 0;
138 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
139 ASSERT_TRUE(pdu);
140 // Unlike the Test Spec's sequence diagram, we respond to the peer's I-Frame
141 // with a Receiver Ready (which is always allowed), so our subsequent
142 // I-Frame is actually the third outbound.
143 if (tx_count == 0) {
144 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
145 ASSERT_TRUE(
146 pdu->To<EnhancedControlField>().designates_information_frame());
147 const auto& header = pdu->To<SimpleInformationFrameHeader>();
148 EXPECT_EQ(0, header.tx_seq());
149 EXPECT_EQ(0, header.receive_seq_num());
150 } else if (tx_count == 1) {
151 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
152 ASSERT_TRUE(
153 pdu->To<EnhancedControlField>().designates_supervisory_frame());
154 ASSERT_TRUE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
155 }
156 tx_count++;
157 });
158 bool failure_cb_called = false;
159 auto failure_cb = [&failure_cb_called] { failure_cb_called = true; };
160 auto [rx_engine, tx_engine] =
161 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
162 kDefaultMTU,
163 /*max_transmissions=*/1,
164 kTxWindow,
165 channel(),
166 failure_cb,
167 dispatcher());
168 ASSERT_TRUE(rx_engine);
169 ASSERT_TRUE(tx_engine);
170
171 TxProcessSdu(*tx_engine,
172 std::make_unique<DynamicByteBuffer>(
173 StaticByteBuffer{'p', 'i', 'n', 'g'}));
174 EXPECT_EQ(1, tx_count);
175
176 // Send a poll request after timer expiry waiting for peer acknowledgment.
177 RETURN_IF_FATAL(RunFor(kErtmReceiverReadyPollTimerDuration));
178 EXPECT_EQ(2, tx_count);
179
180 // Monitor Timer expires without a response from the peer, signaling a channel
181 // failure.
182 EXPECT_FALSE(failure_cb_called);
183 RETURN_IF_FATAL(RunFor(kErtmMonitorTimerDuration));
184 EXPECT_TRUE(failure_cb_called);
185 }
186
187 // This test simulates non-acknowledgment of an I-Frame that the local host
188 // sends which causes us to meet the MaxTransmit that the peer specified,
189 // raising a local error for the channel. This mirrors L2CAP Test Specification
190 // v5.0.2 L2CAP/ERM/BV-12-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,SignalFailureAfterMaxTransmitExhausted)191 TEST_F(EnhancedRetransmissionModeEnginesTest,
192 SignalFailureAfterMaxTransmitExhausted) {
193 int tx_count = 0;
194 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
195 ASSERT_TRUE(pdu);
196 // Unlike the Test Spec's sequence diagram, we respond to the peer's I-Frame
197 // with a Receiver Ready (which is always allowed), so our subsequent
198 // I-Frame is actually the third outbound.
199 if (tx_count == 0) {
200 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
201 ASSERT_TRUE(
202 pdu->To<EnhancedControlField>().designates_information_frame());
203 const auto& header = pdu->To<SimpleInformationFrameHeader>();
204 EXPECT_EQ(0, header.tx_seq());
205 EXPECT_EQ(0, header.receive_seq_num());
206 } else if (tx_count == 1) {
207 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
208 ASSERT_TRUE(
209 pdu->To<EnhancedControlField>().designates_supervisory_frame());
210 ASSERT_TRUE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
211 }
212 tx_count++;
213 });
214 bool failure_cb_called = false;
215 auto failure_cb = [&failure_cb_called] { failure_cb_called = true; };
216 auto [rx_engine, tx_engine] =
217 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
218 kDefaultMTU,
219 /*max_transmissions=*/1,
220 kTxWindow,
221 channel(),
222 failure_cb,
223 dispatcher());
224 ASSERT_TRUE(rx_engine);
225 ASSERT_TRUE(tx_engine);
226
227 TxProcessSdu(*tx_engine,
228 std::make_unique<DynamicByteBuffer>(
229 StaticByteBuffer{'p', 'i', 'n', 'g'}));
230 EXPECT_EQ(1, tx_count);
231
232 // Send a poll request after timer expiry waiting for peer acknowledgment.
233 RETURN_IF_FATAL(RunFor(kErtmReceiverReadyPollTimerDuration));
234 EXPECT_EQ(2, tx_count);
235
236 // Peer response doesn't acknowledge the I-Frame's TxSeq and we already used
237 // up MaxTransmit, signaling a channel failure.
238 EXPECT_FALSE(failure_cb_called);
239
240 // Receive an S-frame containing an acknowledgment not including the frame
241 // that we transmitted. F is set. See Core Spec, v5, Vol 3, Part A,
242 // Section 3.3.2, S-Frame Enhanced Control Field.
243 StaticByteBuffer receiver_ready(0b1 | kExtendedControlFBitMask, 0);
244 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
245 .BuildFrame(kTestChannelId,
246 receiver_ready,
247 FrameCheckSequenceOption::kIncludeFcs));
248 EXPECT_TRUE(failure_cb_called);
249 }
250
251 // This tests the integration of receiving Reject frames with triggering
252 // retransmission of requested unacknowledged I-Frames. This mirrors the L2CAP
253 // Test Specification v5.0.2 L2CAP/ERM/BV-13-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitAfterReceivingRejectSFrame)254 TEST_F(EnhancedRetransmissionModeEnginesTest,
255 RetransmitAfterReceivingRejectSFrame) {
256 int tx_count = 0;
257 int iframe_0_tx_count = 0;
258 int iframe_1_tx_count = 0;
259 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
260 tx_count++;
261 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
262 ASSERT_TRUE(pdu->To<EnhancedControlField>().designates_information_frame());
263 const auto& header = pdu->To<SimpleInformationFrameHeader>();
264 if (header.tx_seq() == 0) {
265 iframe_0_tx_count++;
266 } else if (header.tx_seq() == 1) {
267 iframe_1_tx_count++;
268 }
269 });
270 auto [rx_engine, tx_engine] =
271 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
272 kDefaultMTU,
273 kMaxTransmissions,
274 kTxWindow,
275 channel(),
276 NoOpFailureCallback,
277 dispatcher());
278 ASSERT_TRUE(rx_engine);
279 ASSERT_TRUE(tx_engine);
280
281 TxProcessSdu(*tx_engine,
282 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('a')));
283 EXPECT_EQ(1, tx_count);
284 EXPECT_EQ(1, iframe_0_tx_count);
285 EXPECT_EQ(0, iframe_1_tx_count);
286
287 TxProcessSdu(*tx_engine,
288 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('b')));
289 EXPECT_EQ(2, tx_count);
290 EXPECT_EQ(1, iframe_0_tx_count);
291 EXPECT_EQ(1, iframe_1_tx_count);
292
293 // Receive an S-frame containing a retransmission request starting at seq = 0.
294 // See Core Spec v5.0, Vol 3, Part A, Section 3.3.2 S-Frame Enhanced Control
295 // Field.
296 StaticByteBuffer reject(0b1 | kExtendedControlRejFunctionMask, 0);
297 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
298 .BuildFrame(kTestChannelId,
299 reject,
300 FrameCheckSequenceOption::kIncludeFcs));
301
302 // Check that this caused a retransmission of the two I-Frames.
303 EXPECT_EQ(4, tx_count);
304 EXPECT_EQ(2, iframe_0_tx_count);
305 EXPECT_EQ(2, iframe_1_tx_count);
306 }
307
308 // This tests the integration of receiving Selective Reject frames that are poll
309 // requests, triggering retransmission as well as acknowledgment that frees up
310 // transmit window. This mirrors the L2CAP Test Specification v5.0.2
311 // L2CAP/ERM/BV-14-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitAfterReceivingSelectiveRejectPollRequestSFrame)312 TEST_F(EnhancedRetransmissionModeEnginesTest,
313 RetransmitAfterReceivingSelectiveRejectPollRequestSFrame) {
314 int tx_count = 0;
315 std::array<int, 4> iframe_tx_counts{};
316 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
317 tx_count++;
318 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
319 ASSERT_TRUE(pdu->To<EnhancedControlField>().designates_information_frame());
320 const auto& header = pdu->To<SimpleInformationFrameHeader>();
321 ASSERT_GT(iframe_tx_counts.size(), header.tx_seq());
322 iframe_tx_counts[header.tx_seq()]++;
323 });
324 auto [rx_engine, tx_engine] =
325 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
326 kDefaultMTU,
327 kMaxTransmissions,
328 /*n_frames_in_tx_window=*/3,
329 channel(),
330 NoOpFailureCallback,
331 dispatcher());
332 ASSERT_TRUE(rx_engine);
333 ASSERT_TRUE(tx_engine);
334
335 for (int i = 0; i < 4; i++) {
336 TxProcessSdu(
337 *tx_engine,
338 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('a' + i)));
339 }
340
341 // TxWindow caps transmissions.
342 EXPECT_EQ(3, tx_count);
343 EXPECT_THAT(iframe_tx_counts, testing::ElementsAre(1, 1, 1, 0));
344
345 // Receive an S-frame containing a poll request retransmission request for seq
346 // = 1. See Core Spec v5.0, Vol 3, Part A, Section 3.3.2 S-Frame Enhanced
347 // Control Field.
348 StaticByteBuffer selective_reject(
349 0b1 | kExtendedControlSrejFunctionMask | kExtendedControlPBitMask, 1);
350 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
351 .BuildFrame(kTestChannelId,
352 selective_reject,
353 FrameCheckSequenceOption::kIncludeFcs));
354
355 // Check that this caused a retransmission of I-Frames 1 and 3 because the
356 // poll request acknowledged I-Frame 0, freeing up space in the transmit
357 // window.
358 EXPECT_EQ(5, tx_count);
359 EXPECT_THAT(iframe_tx_counts, testing::ElementsAre(1, 2, 1, 1));
360 }
361
362 // This tests the integration of receiving Selective Reject frames that are not
363 // poll requests, triggering retransmission only. This mirrors the L2CAP Test
364 // Specification v5.0.2 L2CAP/ERM/BV-15-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitAfterReceivingSelectiveRejectSFrame)365 TEST_F(EnhancedRetransmissionModeEnginesTest,
366 RetransmitAfterReceivingSelectiveRejectSFrame) {
367 int tx_count = 0;
368 std::array<int, 4> iframe_tx_counts{};
369 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
370 tx_count++;
371 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
372 ASSERT_TRUE(pdu->To<EnhancedControlField>().designates_information_frame());
373 const auto& header = pdu->To<SimpleInformationFrameHeader>();
374 ASSERT_GT(iframe_tx_counts.size(), header.tx_seq());
375 iframe_tx_counts[header.tx_seq()]++;
376 });
377 auto [rx_engine, tx_engine] =
378 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
379 kDefaultMTU,
380 kMaxTransmissions,
381 /*n_frames_in_tx_window=*/3,
382 channel(),
383 NoOpFailureCallback,
384 dispatcher());
385 ASSERT_TRUE(rx_engine);
386 ASSERT_TRUE(tx_engine);
387
388 for (int i = 0; i < 4; i++) {
389 TxProcessSdu(
390 *tx_engine,
391 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('a' + i)));
392 }
393
394 // TxWindow caps transmissions.
395 EXPECT_EQ(3, tx_count);
396 EXPECT_THAT(iframe_tx_counts, testing::ElementsAre(1, 1, 1, 0));
397
398 // Receive an S-frame containing a retransmission request for seq = 1. See
399 // Core Spec v5.0, Vol 3, Part A, Section 3.3.2 S-Frame Enhanced Control
400 // Field.
401 StaticByteBuffer selective_reject(0b1 | kExtendedControlSrejFunctionMask, 1);
402 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
403 .BuildFrame(kTestChannelId,
404 selective_reject,
405 FrameCheckSequenceOption::kIncludeFcs));
406
407 // Check that this caused a retransmission of I-Frame 1 only.
408 EXPECT_EQ(4, tx_count);
409 EXPECT_THAT(iframe_tx_counts, testing::ElementsAre(1, 2, 1, 0));
410
411 // Receive an S-frame containing an acknowledgment up to seq = 3.
412 StaticByteBuffer ack_3(0b1, 3);
413 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
414 .BuildFrame(kTestChannelId,
415 ack_3,
416 FrameCheckSequenceOption::kIncludeFcs));
417
418 // Initial transmission of I-Frame 3 after the acknowledgment freed up
419 // transmit window capacity.
420 EXPECT_EQ(5, tx_count);
421 EXPECT_THAT(iframe_tx_counts, testing::ElementsAre(1, 2, 1, 1));
422
423 // Receive an S-frame containing an acknowledgment up to seq = 3.
424 StaticByteBuffer ack_4(0b1, 4);
425 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
426 .BuildFrame(kTestChannelId,
427 ack_4,
428 FrameCheckSequenceOption::kIncludeFcs));
429
430 EXPECT_EQ(5, tx_count);
431 }
432
433 // This tests the integration of receiving an acknowledgment sequence with
434 // triggering retransmission of unacknowledged I-Frames. This mirrors the L2CAP
435 // Test Specification L2CAP/ERM/BV-18-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitAfterPollResponseDoesNotAcknowledgeSentFrames)436 TEST_F(EnhancedRetransmissionModeEnginesTest,
437 RetransmitAfterPollResponseDoesNotAcknowledgeSentFrames) {
438 DynamicByteBuffer info_frame;
439 int tx_count = 0;
440 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
441 // The first packet is the I-Frame containing the data that we sent.
442 // The second packet is the S-Frame polling for the peer after the
443 // Retransmission Timer expires. It is not checked to keep the tests less
444 // fragile, as the first two packets are already covered by
445 // EnhancedRetransmissionModeTxEngineTest.
446 if (tx_count == 0) {
447 info_frame = DynamicByteBuffer(*pdu);
448 } else if (tx_count == 2) {
449 EXPECT_TRUE(ContainersEqual(info_frame, *pdu));
450 }
451 tx_count++;
452 });
453 auto [rx_engine, tx_engine] =
454 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
455 kDefaultMTU,
456 kMaxTransmissions,
457 kTxWindow,
458 channel(),
459 NoOpFailureCallback,
460 dispatcher());
461 ASSERT_TRUE(rx_engine);
462 ASSERT_TRUE(tx_engine);
463
464 TxProcessSdu(*tx_engine,
465 std::make_unique<DynamicByteBuffer>(StaticByteBuffer{'a'}));
466 EXPECT_EQ(1, tx_count);
467
468 RunFor(kErtmReceiverReadyPollTimerDuration);
469 EXPECT_EQ(2, tx_count);
470
471 // Receive an S-frame containing an acknowledgment not including the frame
472 // that we transmitted. F is set. See Core Spec, v5, Vol 3, Part A,
473 // Section 3.3.2, S-Frame Enhanced Control Field.
474 StaticByteBuffer receiver_ready(0b1 | kExtendedControlFBitMask, 0);
475 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
476 .BuildFrame(kTestChannelId,
477 receiver_ready,
478 FrameCheckSequenceOption::kIncludeFcs));
479
480 // Check that this caused a retransmission of the initial I-Frame.
481 EXPECT_EQ(3, tx_count);
482 }
483
484 // This test simulates the peer declaring that it is busy by sending the
485 // ReceiverNotReady S-Frame, which prevents us from retransmitting an
486 // unacknowledged outbound I-Frame. This mirrors L2CAP Test Specification v5.0.2
487 // L2CAP/ERM/BV-20-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,DoNotRetransmitAfterReceivingReceiverNotReadyPollResponse)488 TEST_F(EnhancedRetransmissionModeEnginesTest,
489 DoNotRetransmitAfterReceivingReceiverNotReadyPollResponse) {
490 int tx_count = 0;
491 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
492 ASSERT_TRUE(pdu);
493 // Unlike the Test Spec's sequence diagram, we respond to the peer's I-Frame
494 // with a Receiver Ready (which is always allowed), so our subsequent
495 // I-Frame is actually the third outbound.
496 if (tx_count == 0) {
497 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
498 ASSERT_TRUE(
499 pdu->To<EnhancedControlField>().designates_information_frame());
500 const auto& header = pdu->To<SimpleInformationFrameHeader>();
501 EXPECT_EQ(0, header.tx_seq());
502 EXPECT_EQ(0, header.receive_seq_num());
503 } else if (tx_count == 1) {
504 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
505 ASSERT_TRUE(
506 pdu->To<EnhancedControlField>().designates_supervisory_frame());
507 ASSERT_TRUE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
508 }
509 tx_count++;
510 });
511 auto [rx_engine, tx_engine] =
512 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
513 kDefaultMTU,
514 kMaxTransmissions,
515 kTxWindow,
516 channel(),
517 NoOpFailureCallback,
518 dispatcher());
519 ASSERT_TRUE(rx_engine);
520 ASSERT_TRUE(tx_engine);
521
522 TxProcessSdu(*tx_engine,
523 std::make_unique<DynamicByteBuffer>(
524 StaticByteBuffer{'p', 'i', 'n', 'g'}));
525 EXPECT_EQ(1, tx_count);
526
527 // Send a poll request after timer expiry waiting for peer acknowledgment.
528 RETURN_IF_FATAL(RunFor(kErtmReceiverReadyPollTimerDuration));
529 EXPECT_EQ(2, tx_count);
530
531 // Receive an Receiver Not Ready containing an acknowledgment not including
532 // the frame that we transmitted. F is set. See Core Spec, v5, Vol 3, Part A,
533 // Section 3.3.2, S-Frame Enhanced Control Field.
534 StaticByteBuffer receiver_not_ready(
535 0b1 | kExtendedControlReceiverNotReadyFunctionMask |
536 kExtendedControlFBitMask,
537 0);
538 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
539 .BuildFrame(kTestChannelId,
540 receiver_not_ready,
541 FrameCheckSequenceOption::kIncludeFcs));
542
543 // RNR sets our RemoteBusy flag, so we should not transmit anything.
544 EXPECT_EQ(2, tx_count);
545 }
546 // This tests that explicitly a requested selective retransmission and another
547 // for the same I-Frame that is a poll response causes only one retransmission.
548 // This mirrors the L2CAP Test Specification v5.0.2 L2CAP/ERM/BI-03-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitOnlyOnceAfterReceivingDuplicateSelectiveRejectSFramesForSameIFrameDuringReceiverReadyPoll)549 TEST_F(
550 EnhancedRetransmissionModeEnginesTest,
551 RetransmitOnlyOnceAfterReceivingDuplicateSelectiveRejectSFramesForSameIFrameDuringReceiverReadyPoll) {
552 int tx_count = 0;
553 int iframe_0_tx_count = 0;
554 int iframe_1_tx_count = 0;
555 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
556 tx_count++;
557
558 // After outbound I-Frames, expect an outbound poll request S-Frame.
559 if (tx_count == 3) {
560 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
561 ASSERT_TRUE(
562 pdu->To<EnhancedControlField>().designates_supervisory_frame());
563 ASSERT_TRUE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
564 return;
565 }
566
567 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
568 ASSERT_TRUE(pdu->To<EnhancedControlField>().designates_information_frame());
569 const auto& header = pdu->To<SimpleInformationFrameHeader>();
570 if (header.tx_seq() == 0) {
571 iframe_0_tx_count++;
572 } else if (header.tx_seq() == 1) {
573 iframe_1_tx_count++;
574 }
575 });
576 auto [rx_engine, tx_engine] =
577 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
578 kDefaultMTU,
579 kMaxTransmissions,
580 kTxWindow,
581 channel(),
582 NoOpFailureCallback,
583 dispatcher());
584 ASSERT_TRUE(rx_engine);
585 ASSERT_TRUE(tx_engine);
586
587 TxProcessSdu(*tx_engine,
588 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('a')));
589 TxProcessSdu(*tx_engine,
590 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('b')));
591 EXPECT_EQ(2, tx_count);
592 EXPECT_EQ(1, iframe_0_tx_count);
593 EXPECT_EQ(1, iframe_1_tx_count);
594
595 RunFor(kErtmReceiverReadyPollTimerDuration);
596 EXPECT_EQ(3, tx_count);
597
598 // Receive an S-frame containing a retransmission request for I-Frame 0. See
599 // Core Spec v5.0, Vol 3, Part A, Section 3.3.2 S-Frame Enhanced Control
600 // Field.
601 StaticByteBuffer selective_reject(0b1 | kExtendedControlSrejFunctionMask, 0);
602 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
603 .BuildFrame(kTestChannelId,
604 selective_reject,
605 FrameCheckSequenceOption::kIncludeFcs));
606
607 // Receive an S-frame containing a retransmission request for I-Frame 0 and a
608 // poll response.
609 StaticByteBuffer selective_reject_f(
610 0b1 | kExtendedControlSrejFunctionMask | kExtendedControlFBitMask, 0);
611 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
612 .BuildFrame(kTestChannelId,
613 selective_reject_f,
614 FrameCheckSequenceOption::kIncludeFcs));
615
616 // Check that these two SREJs caused a retransmission of I-Frame 0 only once.
617 EXPECT_EQ(4, tx_count);
618 EXPECT_EQ(2, iframe_0_tx_count);
619 EXPECT_EQ(1, iframe_1_tx_count);
620
621 // Acknowledge all of the outbound I-Frames per the specification's sequence
622 // diagram.
623 StaticByteBuffer receiver_ready_2(0b1, 2);
624 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
625 .BuildFrame(kTestChannelId,
626 receiver_ready_2,
627 FrameCheckSequenceOption::kIncludeFcs));
628 EXPECT_EQ(4, tx_count);
629 }
630
631 // This tests that explicitly requested retransmission and receiving a poll
632 // response that doesn't acknowledge all I-Frames results in only a single set
633 // of retransmissions. This mirrors the L2CAP Test Specification v5.0.2
634 // L2CAP/ERM/BI-04-C.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitAfterReceivingRejectSFrameAndReceiverReadyPollResponseNotAcknowledgingSentFrames)635 TEST_F(
636 EnhancedRetransmissionModeEnginesTest,
637 RetransmitAfterReceivingRejectSFrameAndReceiverReadyPollResponseNotAcknowledgingSentFrames) {
638 int tx_count = 0;
639 int iframe_0_tx_count = 0;
640 int iframe_1_tx_count = 0;
641 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
642 tx_count++;
643
644 // After outbound I-Frames, expect an outbound poll request S-Frame.
645 if (tx_count == 3) {
646 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
647 ASSERT_TRUE(
648 pdu->To<EnhancedControlField>().designates_supervisory_frame());
649 ASSERT_TRUE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
650 return;
651 }
652
653 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
654 ASSERT_TRUE(pdu->To<EnhancedControlField>().designates_information_frame());
655 const auto& header = pdu->To<SimpleInformationFrameHeader>();
656 if (header.tx_seq() == 0) {
657 iframe_0_tx_count++;
658 } else if (header.tx_seq() == 1) {
659 iframe_1_tx_count++;
660 }
661 });
662 auto [rx_engine, tx_engine] =
663 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
664 kDefaultMTU,
665 kMaxTransmissions,
666 kTxWindow,
667 channel(),
668 NoOpFailureCallback,
669 dispatcher());
670 ASSERT_TRUE(rx_engine);
671 ASSERT_TRUE(tx_engine);
672
673 TxProcessSdu(*tx_engine,
674 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('a')));
675 TxProcessSdu(*tx_engine,
676 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('b')));
677 EXPECT_EQ(2, tx_count);
678 EXPECT_EQ(1, iframe_0_tx_count);
679 EXPECT_EQ(1, iframe_1_tx_count);
680
681 RunFor(kErtmReceiverReadyPollTimerDuration);
682 EXPECT_EQ(3, tx_count);
683
684 // Receive an S-frame containing a retransmission request starting at seq = 0.
685 // See Core Spec v5.0, Vol 3, Part A, Section 3.3.2 S-Frame Enhanced Control
686 // Field.
687 StaticByteBuffer reject(0b1 | kExtendedControlRejFunctionMask, 0);
688 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
689 .BuildFrame(kTestChannelId,
690 reject,
691 FrameCheckSequenceOption::kIncludeFcs));
692
693 // Receive an S-frame containing an acknowledgment not including the frames
694 // that we transmitted. F is set. See Core Spec v5.0, Vol 3, Part A,
695 // Section 3.3.2 S-Frame Enhanced Control Field.
696 StaticByteBuffer receiver_ready_0(0b1 | kExtendedControlFBitMask, 0);
697 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
698 .BuildFrame(kTestChannelId,
699 receiver_ready_0,
700 FrameCheckSequenceOption::kIncludeFcs));
701
702 // Check that this caused a retransmission of the two I-Frames only once each.
703 EXPECT_EQ(5, tx_count);
704 EXPECT_EQ(2, iframe_0_tx_count);
705 EXPECT_EQ(2, iframe_1_tx_count);
706
707 // Acknowledge all of the outbound I-Frames per the specification's sequence
708 // diagram.
709 StaticByteBuffer receiver_ready_2(0b1, 2);
710 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
711 .BuildFrame(kTestChannelId,
712 receiver_ready_2,
713 FrameCheckSequenceOption::kIncludeFcs));
714 EXPECT_EQ(5, tx_count);
715 }
716
717 // This tests that explicitly requested retransmission and receiving a I-Frame
718 // that doesn't acknowledge all I-Frames results in only a single set of
719 // retransmissions. This mirrors the L2CAP Test Specification v5.0.2
720 // L2CAP/ERM/BI-05-C in which we—the IUT—perform the ALT1 path in the sequence
721 // diagram Figure 4.96.
TEST_F(EnhancedRetransmissionModeEnginesTest,RetransmitAfterReceivingRejectSFrameAndIFramePollResponseNotAcknowledgingSentFrames)722 TEST_F(
723 EnhancedRetransmissionModeEnginesTest,
724 RetransmitAfterReceivingRejectSFrameAndIFramePollResponseNotAcknowledgingSentFrames) {
725 int tx_count = 0;
726 int iframe_0_tx_count = 0;
727 int iframe_1_tx_count = 0;
728 channel().HandleSendFrame([&](ByteBufferPtr pdu) {
729 tx_count++;
730 SCOPED_TRACE(tx_count);
731
732 if (tx_count == 3) {
733 // After outbound I-Frames (not retransmitted), expect an outbound poll
734 // request S-Frame.
735 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
736 ASSERT_TRUE(
737 pdu->To<EnhancedControlField>().designates_supervisory_frame());
738 EXPECT_TRUE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
739 return;
740 } else if (tx_count == 6) {
741 // After retransmitted I-Frames, expect an outbound ReceiverReady that
742 // acknowledges the peer's I-Frame.
743 ASSERT_EQ(sizeof(SimpleSupervisoryFrame), pdu->size());
744 ASSERT_TRUE(
745 pdu->To<EnhancedControlField>().designates_supervisory_frame());
746 EXPECT_FALSE(pdu->To<SimpleSupervisoryFrame>().is_poll_request());
747 EXPECT_EQ(1, pdu->To<SimpleSupervisoryFrame>().receive_seq_num());
748 return;
749 }
750
751 ASSERT_LE(sizeof(SimpleInformationFrameHeader), pdu->size());
752 ASSERT_TRUE(pdu->To<EnhancedControlField>().designates_information_frame());
753 const auto& header = pdu->To<SimpleInformationFrameHeader>();
754 EXPECT_EQ(0, header.receive_seq_num());
755 if (header.tx_seq() == 0) {
756 iframe_0_tx_count++;
757 } else if (header.tx_seq() == 1) {
758 iframe_1_tx_count++;
759 }
760 });
761 auto [rx_engine, tx_engine] =
762 MakeLinkedEnhancedRetransmissionModeEngines(kTestChannelId,
763 kDefaultMTU,
764 kMaxTransmissions,
765 kTxWindow,
766 channel(),
767 NoOpFailureCallback,
768 dispatcher());
769 ASSERT_TRUE(rx_engine);
770 ASSERT_TRUE(tx_engine);
771
772 TxProcessSdu(*tx_engine,
773 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('a')));
774 TxProcessSdu(*tx_engine,
775 std::make_unique<DynamicByteBuffer>(StaticByteBuffer('b')));
776 EXPECT_EQ(2, tx_count);
777 EXPECT_EQ(1, iframe_0_tx_count);
778 EXPECT_EQ(1, iframe_1_tx_count);
779
780 RunFor(kErtmReceiverReadyPollTimerDuration);
781 EXPECT_EQ(3, tx_count);
782
783 // Receive an S-frame containing a retransmission request starting at seq = 0.
784 // See Core Spec v5.0, Vol 3, Part A, Section 3.3.2 S-Frame Enhanced Control
785 // Field.
786 StaticByteBuffer reject(0b1 | kExtendedControlRejFunctionMask, 0);
787 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
788 .BuildFrame(kTestChannelId,
789 reject,
790 FrameCheckSequenceOption::kIncludeFcs));
791
792 // Receive an I-frame containing an acknowledgment not including the frames
793 // that we transmitted. F is set. See Core Spec v5.0, Vol 3, Part A,
794 // Section 3.3.2 I-Frame Enhanced Control Field.
795 StaticByteBuffer inbound_iframe(kExtendedControlFBitMask, 0, 'A');
796 auto inbound_pdu = rx_engine->ProcessPdu(
797 Fragmenter(kTestHandle)
798 .BuildFrame(kTestChannelId,
799 inbound_iframe,
800 FrameCheckSequenceOption::kIncludeFcs));
801 EXPECT_TRUE(inbound_pdu);
802
803 // Check that this caused a retransmission of the two I-Frames only once each,
804 // plus our acknowledgment of the peer I-Frame.
805 EXPECT_EQ(6, tx_count);
806 EXPECT_EQ(2, iframe_0_tx_count);
807 EXPECT_EQ(2, iframe_1_tx_count);
808
809 // Acknowledge all of the outbound I-Frames per the specification's sequence
810 // diagram.
811 StaticByteBuffer receiver_ready(0b1, 2);
812 rx_engine->ProcessPdu(Fragmenter(kTestHandle)
813 .BuildFrame(kTestChannelId,
814 receiver_ready,
815 FrameCheckSequenceOption::kIncludeFcs));
816 EXPECT_EQ(6, tx_count);
817 }
818
819 } // namespace
820 } // namespace bt::l2cap::internal
821