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