• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_tx_engine.h"
16 
17 #include <limits>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/frame_headers.h"
22 
23 namespace bt::l2cap::internal {
24 
25 using Engine = EnhancedRetransmissionModeTxEngine;
26 
27 namespace {
28 
29 // Returns the number of frames within the range from |low| to |high|, inclusive
30 // of |low|, but exclusive of |high|. Returns zero if |low == high|.
NumFramesBetween(uint8_t low,uint8_t high)31 uint8_t NumFramesBetween(uint8_t low, uint8_t high) {
32   if (high < low) {
33     high += (EnhancedControlField::kMaxSeqNum + 1);
34   }
35   return high - low;
36 }
37 
38 }  // namespace
39 
EnhancedRetransmissionModeTxEngine(ChannelId channel_id,uint16_t max_tx_sdu_size,uint8_t max_transmissions,uint8_t n_frames_in_tx_window,SendFrameCallback send_frame_callback,ConnectionFailureCallback connection_failure_callback,pw::async::Dispatcher & dispatcher)40 Engine::EnhancedRetransmissionModeTxEngine(
41     ChannelId channel_id,
42     uint16_t max_tx_sdu_size,
43     uint8_t max_transmissions,
44     uint8_t n_frames_in_tx_window,
45     SendFrameCallback send_frame_callback,
46     ConnectionFailureCallback connection_failure_callback,
47     pw::async::Dispatcher& dispatcher)
48     : TxEngine(channel_id, max_tx_sdu_size, std::move(send_frame_callback)),
49       pw_dispatcher_(dispatcher),
50       max_transmissions_(max_transmissions),
51       n_frames_in_tx_window_(n_frames_in_tx_window),
52       connection_failure_callback_(std::move(connection_failure_callback)),
53       expected_ack_seq_(0),
54       next_tx_seq_(0),
55       last_tx_seq_(0),
56       req_seqnum_(0),
57       retransmitted_range_during_poll_(false),
58       n_receiver_ready_polls_sent_(0),
59       remote_is_busy_(false) {
60   BT_DEBUG_ASSERT(n_frames_in_tx_window_);
61   receiver_ready_poll_task_.set_function(
62       [this](pw::async::Context /*ctx*/, pw::Status status) {
63         if (!status.ok()) {
64           return;
65         }
66         SendReceiverReadyPoll();
67         StartMonitorTimer();
68       });
69   monitor_task_.set_function(
70       [this](pw::async::Context /*ctx*/, pw::Status status) {
71         if (!status.ok()) {
72           return;
73         }
74         if (max_transmissions_ == 0 ||
75             n_receiver_ready_polls_sent_ < max_transmissions_) {
76           SendReceiverReadyPoll();
77           StartMonitorTimer();
78         } else {
79           connection_failure_callback_();  // May invalidate |self|.
80         }
81       });
82 }
83 
QueueSdu(ByteBufferPtr sdu)84 bool Engine::QueueSdu(ByteBufferPtr sdu) {
85   BT_ASSERT(sdu);
86   // TODO(fxbug.dev/42054330): Add support for segmentation
87   if (sdu->size() > max_tx_sdu_size_) {
88     bt_log(INFO,
89            "l2cap",
90            "SDU size exceeds channel TxMTU (channel-id: %#.4x)",
91            channel_id_);
92     return false;
93   }
94 
95   const auto seq_num = GetNextTxSeq();
96   SimpleInformationFrameHeader header(seq_num);
97   DynamicByteBuffer frame(sizeof(header) + sdu->size());
98   auto body = frame.mutable_view(sizeof(header));
99   frame.WriteObj(header);
100   sdu->Copy(&body);
101 
102   // TODO(fxbug.dev/42086227): Limit the size of the queue.
103   pending_pdus_.push_back(std::move(frame));
104   MaybeSendQueuedData();
105   return true;
106 }
107 
UpdateAckSeq(uint8_t new_seq,bool is_poll_response)108 void Engine::UpdateAckSeq(uint8_t new_seq, bool is_poll_response) {
109   // TODO(quiche): Reconsider this assertion if we allow reconfiguration of the
110   // TX window.
111   BT_DEBUG_ASSERT_MSG(NumUnackedFrames() <= n_frames_in_tx_window_,
112                       "(NumUnackedFrames() = %u, n_frames_in_tx_window_ = %u, "
113                       "expected_ack_seq_ = %u, last_tx_seq_ = %u)",
114                       NumUnackedFrames(),
115                       n_frames_in_tx_window_,
116                       expected_ack_seq_,
117                       last_tx_seq_);
118 
119   const auto n_frames_acked = NumFramesBetween(expected_ack_seq_, new_seq);
120   if (n_frames_acked > NumUnackedFrames()) {
121     // Peer acknowledgment of our outbound data (ReqSeq) exceeds the sequence
122     // numbers of yet-acknowledged data that we've sent to that peer. See
123     // conditions "With-Invalid-ReqSeq" and "With-Invalid-ReqSeq-Retrans" in
124     // Core Spec v5.0 Vol 3 Part A Sec 8.6.5.5.
125     bt_log(WARN,
126            "l2cap",
127            "Received acknowledgment for %hhu frames but only %hhu frames are "
128            "pending",
129            n_frames_acked,
130            NumUnackedFrames());
131     connection_failure_callback_();  // May invalidate |self|.
132     return;
133   }
134 
135   // Perform Stop-MonitorTimer when "F = 1," per Core Spec v5.0, Vol 3, Part A,
136   // Sec 8.6.5.7–8.
137   if (is_poll_response) {
138     monitor_task_.Cancel();
139   }
140 
141   BT_ASSERT(!(range_request_.has_value() && single_request_.has_value()));
142   if (ProcessSingleRetransmitRequest(new_seq, is_poll_response) ==
143       UpdateAckSeqAction::kConsumeAckSeq) {
144     return;
145   }
146 
147   auto n_frames_to_discard = n_frames_acked;
148   while (n_frames_to_discard) {
149     BT_DEBUG_ASSERT(!pending_pdus_.empty());
150     pending_pdus_.pop_front();
151     --n_frames_to_discard;
152   }
153 
154   expected_ack_seq_ = new_seq;
155   if (expected_ack_seq_ == next_tx_seq_) {
156     receiver_ready_poll_task_.Cancel();
157   }
158 
159   const auto range_request = std::exchange(range_request_, std::nullopt);
160 
161   // RemoteBusy is cleared as the first action to take when receiving a REJ per
162   // Core Spec v5.0 Vol 3, Part A, Sec 8.6.5.9–11, so their corresponding member
163   // variables shouldn't be both set.
164   BT_ASSERT(!(range_request.has_value() && remote_is_busy_));
165   bool should_retransmit = range_request.has_value();
166 
167   // This implements the logic for RejActioned in the Recv {I,RR,REJ} (F=1)
168   // event for all of the receiver states (Core Spec v5.0 Vol 3, Part A,
169   // Sec 8.6.5.9–11).
170   if (is_poll_response) {
171     if (retransmitted_range_during_poll_) {
172       should_retransmit = false;
173       retransmitted_range_during_poll_ = false;
174     } else {
175       should_retransmit = true;
176     }
177   }
178 
179   if (remote_is_busy_) {
180     return;
181   }
182 
183   // This implements the logic for PbitOutstanding in the Recv REJ (F=0) event
184   // for all of the receiver states (Core Spec v5.0 Vol 3, Part A,
185   // Sec 8.6.5.9–11).
186   if (range_request.has_value() && !is_poll_response &&
187       monitor_task_.is_pending()) {
188     retransmitted_range_during_poll_ = true;
189   }
190 
191   if (should_retransmit) {
192     const bool set_is_poll_response =
193         range_request.value_or(RangeRetransmitRequest{}).is_poll_request;
194     if (!RetransmitUnackedData(std::nullopt, set_is_poll_response)) {
195       return;
196     }
197   }
198 
199   MaybeSendQueuedData();
200 
201   // TODO(quiche): Restart the receiver_ready_poll_task_, if there's any
202   // remaining unacknowledged data.
203 }
204 
UpdateReqSeq(uint8_t new_seq)205 void Engine::UpdateReqSeq(uint8_t new_seq) { req_seqnum_ = new_seq; }
206 
ClearRemoteBusy()207 void Engine::ClearRemoteBusy() {
208   // TODO(quiche): Maybe clear backpressure on the Channel (subject to TxWindow
209   // contraints).
210   remote_is_busy_ = false;
211 }
212 
SetRemoteBusy()213 void Engine::SetRemoteBusy() {
214   // TODO(fxbug.dev/42086338): Signal backpressure to the Channel.
215   remote_is_busy_ = true;
216   receiver_ready_poll_task_.Cancel();
217 }
218 
SetSingleRetransmit(bool is_poll_request)219 void Engine::SetSingleRetransmit(bool is_poll_request) {
220   BT_ASSERT(!single_request_.has_value());
221   BT_ASSERT(!range_request_.has_value());
222   // Store SREJ state for UpdateAckSeq to handle.
223   single_request_ = SingleRetransmitRequest{.is_poll_request = is_poll_request};
224 }
225 
SetRangeRetransmit(bool is_poll_request)226 void Engine::SetRangeRetransmit(bool is_poll_request) {
227   BT_ASSERT(!single_request_.has_value());
228   BT_ASSERT(!range_request_.has_value());
229   // Store REJ state for UpdateAckSeq to handle.
230   range_request_ = RangeRetransmitRequest{.is_poll_request = is_poll_request};
231 }
232 
MaybeSendQueuedData()233 void Engine::MaybeSendQueuedData() {
234   if (remote_is_busy_ || monitor_task_.is_pending()) {
235     return;
236   }
237 
238   // Find the first PDU that has not already been transmitted (if any).
239   // * This is not necessarily the first PDU, because that may have been
240   //   transmited already, and is just pending acknowledgement.
241   // * This is not necessarily the last PDU, because earlier PDUs may have been
242   //   queued without having been sent over-the-air (due, e.g., to tx_window
243   //   constraints).
244   //
245   // TODO(quiche): Consider if there's a way to do this that isn't O(n).
246   auto it = std::find_if(
247       pending_pdus_.begin(), pending_pdus_.end(), [](const auto& pending_pdu) {
248         return pending_pdu.tx_count == 0;
249       });
250 
251   while (it != pending_pdus_.end() &&
252          NumUnackedFrames() < n_frames_in_tx_window_) {
253     BT_DEBUG_ASSERT(it->tx_count == 0);
254     SendPdu(&*it);
255     last_tx_seq_ = it->buf.To<SimpleInformationFrameHeader>().tx_seq();
256     ++it;
257   }
258 }
259 
ProcessSingleRetransmitRequest(uint8_t new_seq,bool is_poll_response)260 Engine::UpdateAckSeqAction Engine::ProcessSingleRetransmitRequest(
261     uint8_t new_seq, bool is_poll_response) {
262   const auto single_request = std::exchange(single_request_, std::nullopt);
263   BT_ASSERT(!(single_request.has_value() && remote_is_busy_));
264   if (!single_request.has_value()) {
265     return UpdateAckSeqAction::kDiscardAcknowledged;
266   }
267 
268   // This implements the logic for SrejActioned=TRUE in the Recv SREJ (P=0)
269   // (F=1) event for all of the receiver states (Core Spec v5.0 Vol 3, Part A,
270   // Sec 8.6.5.9–11).
271   if (is_poll_response && retransmitted_single_during_poll_.has_value() &&
272       new_seq == *retransmitted_single_during_poll_) {
273     // Only a SREJ (F=1) with a matching AckSeq can clear this state, so if this
274     // duplicate suppression isn't performed it sticks around even when we sent
275     // the next poll request. AckSeq is modulo kMaxSeqNum + 1 so it'll roll over
276     // and potentially suppress retransmission of a different (non-duplicate)
277     // packet in a later poll request-response cycle. Unfortunately this isn't
278     // fixed as of Core Spec v5.2 so it's implemented as written.
279     retransmitted_single_during_poll_.reset();
280 
281     // Return early to suppress duplicate retransmission.
282     return UpdateAckSeqAction::kConsumeAckSeq;
283   }
284 
285   // This implements the logic for PbitOutstanding in the Recv SREJ (P=0) (F=0)
286   // and Recv SREJ(P=1) events for all of the receiver states (Core Spec v5.0
287   // Vol 3, Part A, Sec 8.6.5.9–11).
288   if (!is_poll_response && monitor_task_.is_pending()) {
289     retransmitted_single_during_poll_ = new_seq;
290   }
291 
292   if (!RetransmitUnackedData(new_seq, single_request->is_poll_request)) {
293     return UpdateAckSeqAction::kConsumeAckSeq;
294   }
295 
296   // Only "single requests" that are poll requests acknowledge previous I-Frames
297   // and cause initial transmission of queued SDUs, per Core Spec v5.0, Vol 3,
298   // Part A, Sec 8.6.1.4.
299   if (single_request->is_poll_request) {
300     return UpdateAckSeqAction::kDiscardAcknowledged;
301   }
302   return UpdateAckSeqAction::kConsumeAckSeq;
303 }
304 
StartReceiverReadyPollTimer()305 void Engine::StartReceiverReadyPollTimer() {
306   BT_DEBUG_ASSERT(!monitor_task_.is_pending());
307   n_receiver_ready_polls_sent_ = 0;
308   receiver_ready_poll_task_.Cancel();
309   receiver_ready_poll_task_.PostAfter(kErtmReceiverReadyPollTimerDuration);
310 }
311 
StartMonitorTimer()312 void Engine::StartMonitorTimer() {
313   BT_DEBUG_ASSERT(!receiver_ready_poll_task_.is_pending());
314   monitor_task_.Cancel();
315   monitor_task_.PostAfter(kErtmMonitorTimerDuration);
316 }
317 
SendReceiverReadyPoll()318 void Engine::SendReceiverReadyPoll() {
319   SimpleReceiverReadyFrame frame;
320   frame.set_receive_seq_num(req_seqnum_);
321   frame.set_is_poll_request();
322   ++n_receiver_ready_polls_sent_;
323   BT_ASSERT_MSG(max_transmissions_ == 0 ||
324                     n_receiver_ready_polls_sent_ <= max_transmissions_,
325                 "(n_receiver_ready_polls_sent_ = %u, "
326                 "max_transmissions = %u)",
327                 n_receiver_ready_polls_sent_,
328                 max_transmissions_);
329   send_frame_callback_(
330       std::make_unique<DynamicByteBuffer>(BufferView(&frame, sizeof(frame))));
331 }
332 
GetNextTxSeq()333 uint8_t Engine::GetNextTxSeq() {
334   auto ret = next_tx_seq_;
335   ++next_tx_seq_;
336   if (next_tx_seq_ > EnhancedControlField::kMaxSeqNum) {
337     next_tx_seq_ = 0;
338   }
339   return ret;
340 }
341 
NumUnackedFrames()342 uint8_t Engine::NumUnackedFrames() {
343   if (pending_pdus_.empty()) {
344     // Initially, |ack_seqnum_ == last_tx_seq_ == 0|, but the number of
345     // unacknowledged frames is 0, not 1.
346     return 0;
347   } else if (pending_pdus_.front().tx_count == 0) {
348     // While we have some data queued, none of that data has been sent
349     // over-the-air. This might happen, e.g., transiently in QueueSdu().
350     return 0;
351   } else {
352     // Having ascertained that some data _is_ in flight, the number of frames in
353     // flight is given by the expression below.
354     return NumFramesBetween(
355         expected_ack_seq_,
356         last_tx_seq_ + 1  // Include frame with |last_tx_seq_| in count
357     );
358   }
359 }
360 
SendPdu(PendingPdu * pdu)361 void Engine::SendPdu(PendingPdu* pdu) {
362   BT_DEBUG_ASSERT(pdu);
363   pdu->buf.AsMutable<SimpleInformationFrameHeader>()->set_receive_seq_num(
364       req_seqnum_);
365 
366   // Prevent tx_count from overflowing to zero, as that would be
367   // indistinguishable from "never transmitted." This is only possible when
368   // configured for infinite retransmissions, so there is no benefit to having
369   // an accurate tx_count after each frame's initial transmission.
370   if (pdu->tx_count != std::numeric_limits<decltype(pdu->tx_count)>::max()) {
371     pdu->tx_count++;
372   }
373   StartReceiverReadyPollTimer();
374   send_frame_callback_(std::make_unique<DynamicByteBuffer>(pdu->buf));
375 }
376 
RetransmitUnackedData(std::optional<uint8_t> only_with_seq,bool set_is_poll_response)377 bool Engine::RetransmitUnackedData(std::optional<uint8_t> only_with_seq,
378                                    bool set_is_poll_response) {
379   // The receive engine should have cleared the remote busy condition before
380   // calling any method that would cause us (the transmit engine) to retransmit
381   // unacked data. See, e.g., Core Spec v5.0, Volume 3, Part A, Table 8.6, row
382   // "Recv REJ (F=0)".
383   BT_DEBUG_ASSERT(!remote_is_busy_);
384 
385   // Any peer actions that cause retransmission indicate the peer is alive. This
386   // is in conflict with Core Spec v5.0, Vol 3, Part A, Sec 8.6.5.8, which only
387   // stops the MonitorTimer when a poll response is received and omits what to
388   // do when REJ or SREJ cause retransmission. However, this behavior of
389   // canceling the MonitorTimer "early" is in line with Sequence Diagram
390   // Fig 4.94, L2CAP Test Spec v5.0.2 Section 4.9.7.24, among others that show
391   // REJ or SREJ causing the receiver to cancel its MonitorTimer. We follow the
392   // latter behavior because (1) it's less ambiguous (2) it makes sense and (3)
393   // we need to pass those tests.
394   monitor_task_.Cancel();
395 
396   const auto n_to_send = NumUnackedFrames();
397   BT_ASSERT(n_to_send <= n_frames_in_tx_window_);
398   BT_DEBUG_ASSERT(n_to_send <= pending_pdus_.size());
399 
400   auto cur_frame = pending_pdus_.begin();
401   auto last_frame = std::next(cur_frame, n_to_send);
402   for (; cur_frame != last_frame; cur_frame++) {
403     BT_DEBUG_ASSERT(cur_frame != pending_pdus_.end());
404 
405     const auto control_field =
406         cur_frame->buf.To<SimpleInformationFrameHeader>();
407     if (only_with_seq.has_value() && control_field.tx_seq() != *only_with_seq) {
408       continue;
409     }
410 
411     // Core Spec v5.0, Vol 3, Part A, Sec 5.4: "In Enhanced Retransmission mode
412     // a value of zero for MaxTransmit means infinite retransmissions."
413     if (max_transmissions_ != 0 && cur_frame->tx_count >= max_transmissions_) {
414       BT_ASSERT_MSG(cur_frame->tx_count == max_transmissions_,
415                     "%hhu != %hhu",
416                     cur_frame->tx_count,
417                     max_transmissions_);
418       connection_failure_callback_();
419       return false;
420     }
421 
422     if (set_is_poll_response) {
423       cur_frame->buf.AsMutable<EnhancedControlField>()->set_is_poll_response();
424 
425       // Per "Retransmit-I-frames" of Core Spec v5.0 Vol 3, Part A, Sec 8.6.5.6,
426       // "the F-bit of all other [than the first] unacknowledged I-frames sent
427       // shall be 0," so clear this for subsequent iterations.
428       set_is_poll_response = false;
429     }
430 
431     // TODO(fxbug.dev/42087625): If the task is already running, we should not
432     // restart it.
433     SendPdu(&*cur_frame);
434     *cur_frame->buf.AsMutable<EnhancedControlField>() = control_field;
435   }
436 
437   return true;
438 }
439 
440 }  // namespace bt::l2cap::internal
441