blob: cfde24397ead9663b064fed51f22555250a0e687 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_ENHANCED_RETRANSMISSION_MODE_TX_ENGINE_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_ENHANCED_RETRANSMISSION_MODE_TX_ENGINE_H_
#include <lib/fit/thread_checker.h>
#include <list>
#include "lib/async/cpp/task.h"
#include "lib/fit/function.h"
#include "lib/fit/result.h"
#include "lib/zx/time.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/tx_engine.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt::l2cap::internal {
// Implements the sender-side functionality of L2CAP Enhanced Retransmission
// Mode. See Bluetooth Core Spec v5.0, Volume 3, Part A, Sec 2.4, "Modes of
// Operation".
//
// THREAD-SAFETY: This class may is _not_ thread-safe. In particular:
// * the class assumes that some other party ensures that QueueSdu() is not
// invoked concurrently with the destructor, and
// * the class assumes that all calls to QueueSdu occur on a single thread,
// for the entire lifetime of an object.
class EnhancedRetransmissionModeTxEngine final : public TxEngine {
public:
using ConnectionFailureCallback = fit::function<void()>;
// Create a transmit engine.
//
// The engine will invoke |send_frame_callback| when a PDU is ready for
// transmission; see tx_engine.h for further detail.
//
// The engine will invoke |connection_failure_callback| when a fatal error
// occurs on this connection. This callback _may_ occur synchronously. For
// example, a call to UpdateAckSeq() may synchronously invoke
// |connection_failure_callback|.
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);
~EnhancedRetransmissionModeTxEngine() override = default;
bool QueueSdu(ByteBufferPtr sdu) override;
// Updates the Engine's knowledge of the last frame acknowledged by our peer.
// The value of |is_poll_response| should reflect the 'F' bit in header of the
// frame which led to this call.
//
// * This _may_ trigger retransmission of previously transmitted data.
// * This _may_ cause the (initial) transmission of queued data.
void UpdateAckSeq(uint8_t new_seq, bool is_poll_response);
// Updates the Engine's knowledge of the next frame we expect to receive from
// our peer.
void UpdateReqSeq(uint8_t new_seq);
// Informs the Engine that the peer is able to receive frames.
void ClearRemoteBusy();
// Informs the Engine that the peer is unable to receive additional frames at
// this time.
void SetRemoteBusy();
// Requests that the next UpdateAckSeq always retransmit the frame identified by its |new_seq|
// parameter, even if it's not a poll response. This path fulfills the requirements of receiving
// the SREJ function.
//
// Only one of Set{Single,Range}Retransmit can be called before each UpdateAckSeq.
//
// |is_poll_request| should be set if the request to retransmit has its P bit set and will cause
// the first retransmitted frame to have its |is_poll_response| bit (F bit) set per the
// Retransmit-Requested-I-frame action in Core Spec v5.0 Vol 3, Part A, Sec 8.6.5.6.
//
// If |is_poll_request| is false, UpdateAckSeq will be inhibited from dropping the unacked packets
// from ExpectedAckSeq to |new_seq| because this action is used to selectively retransmit missing
// frames that precede frames that the peer has already received.
//
// ClearRemoteBusy should be called if necessary because RemoteBusy is cleared as the first action
// to take when receiving an SREJ per Core Spec v5.0 Vol 3, Part A, Sec 8.6.5.9–11.
void SetSingleRetransmit(bool is_poll_request);
// Requests that the next UpdateAckSeq always retransmit starting at its |new_seq| parameter, even
// if it's not a poll response. This path fulfills the requirements of receiving the REJ function.
//
// Only one of Set{Single,Range}Retransmit can be called before each UpdateAckSeq.
//
// |is_poll_request| should be set if the request to retransmit has its P bit set and will cause
// the first retransmitted frame to have its |is_poll_response| bit (F bit) set per the
// Retransmit-I-frames action in Core Spec v5.0 Vol 3, Part A, Sec 8.6.5.6.
//
// ClearRemoteBusy should be called if necessary because RemoteBusy is cleared as the first action
// to take when receiving a REJ per Core Spec v5.0 Vol 3, Part A, Sec 8.6.5.9–11.
void SetRangeRetransmit(bool is_poll_request);
// Transmits data that has been queued, but which has never been previously
// sent to our peer. The transmissions are subject to remote-busy and transmit
// window constraints.
void MaybeSendQueuedData();
private:
struct PendingPdu {
PendingPdu(DynamicByteBuffer buf_in) : buf(std::move(buf_in)), tx_count(0){};
DynamicByteBuffer buf;
uint8_t tx_count;
};
// State of a request from a peer to retransmit a frame of unacked data (SREJ per Core Spec v5.0,
// Vol 3, Part A, Sec 8.6.1.4).
struct SingleRetransmitRequest {
// True if the request to retransmit has its P bit set.
bool is_poll_request;
};
// State of a request from a peer to retransmit a range of unacked data (REJ per Core Spec v5.0,
// Vol 3, Part A, Sec 8.6.1.2).
struct RangeRetransmitRequest {
// True if the request to retransmit has its P bit set.
bool is_poll_request;
};
// Actions to take with a new AckSeq from the peer.
enum class UpdateAckSeqAction {
kConsumeAckSeq, // Done with the received AckSeq.
kDiscardAcknowledged, // Discard frames prior to AckSeq then send pending frames.
};
// Called from UpdateAckSeq to check if a SingleRetransmitRequest is pending. Retransmits if
// necessary and returns the action to take with |new_seq|.
//
// This call clears |single_request_| if set, so it is not idempotent.
UpdateAckSeqAction ProcessSingleRetransmitRequest(uint8_t new_seq, bool is_poll_response);
// Starts the receiver ready poll timer. If already running, the existing
// timer is cancelled, and a new timer is started.
// Notes:
// * The specification refers to this as the "retransmission timer". However,
// the expiry of this timer doesn't immediately trigger
// retransmission. Rather, the timer expiration triggers us asking the
// receiver to acknowledge all previously received data. See
// "RetransTimer-Expires", in Core Spec v5.0, Volume 3, Part A, Table 8.4.
// * Replacing the existing timer is required per Core Spec v5.0, Volume 3,
// Part A, Section 8.6.5.6, "Start-RetransTimer".
void StartReceiverReadyPollTimer();
// Starts the monitor timer. If already running, the existing timer is
// cancelled, and a new timer is started.
//
// Note that replacing the existing timer is required per Core Spec v5.0,
// Volume 3, Part A, Section 8.6.5.6, "Start-MonitorTimer".
void StartMonitorTimer();
void SendReceiverReadyPoll();
// Return and consume the next sequence number.
uint8_t GetNextTxSeq();
// Returns the number of frames that have been transmitted but not yet
// acknowledged.
uint8_t NumUnackedFrames();
void SendPdu(PendingPdu* pdu);
// Retransmits frames from |pending_pdus_|. Invokes |connection_failure_callback_| on error and
// returns fit::error(). Cancels |monitor_task_| if it's running.
//
// If |only_with_seq| is set, then only the unacked frame with that TxSeq will be retransmitted.
//
// If |set_is_poll_response| is true, then the first frame to be sent will have its
// |is_poll_response| field set.
//
// Notes:
// * The caller must ensure that |!remote_is_busy_|.
// * When return value is an error, |this| may be invalid.
[[nodiscard]] fit::result<> RetransmitUnackedData(std::optional<uint8_t> only_with_seq,
bool set_is_poll_response);
const uint8_t max_transmissions_;
const uint8_t n_frames_in_tx_window_;
// Invoked when the connection encounters a fatal error.
const ConnectionFailureCallback connection_failure_callback_;
// The sequence number we expect in the next acknowledgement from our peer.
//
// We assume that the Extended Window Size option is _not_ enabled. In such
// cases, the sequence number is a 6-bit counter that wraps on overflow. See
// Core Spec v5.0, Vol 3, Part A, Secs 5.7 and 8.3.
uint8_t expected_ack_seq_;
// The sequence number we will use for the next new outbound I-frame.
//
// We assume that the Extended Window Size option is _not_ enabled. In such
// cases, the sequence number is a 6-bit counter that wraps on overflow. See
// Core Spec v5.0, Vol 3, Part A, Secs 5.7 and 8.3.
uint8_t next_tx_seq_;
// The sequence number of the "newest" transmitted frame.
//
// This sequence number is updated when a new frame is transmitted. This
// excludes cases where a frame is queued but not transmitted (due to transmit
// window limitations), and cases where a frame is retransmitted.
//
// This value is useful for determining the number of frames than are
// in-flight to our peer (frames that have been transmitted but not
// acknowledged).
//
// We assume that the Extended Window Size option is _not_ enabled. In such
// cases, the sequence number is a 6-bit counter that wraps on overflow. See
// Core Spec v5.0, Vol 3, Part A, Secs 5.7 and 8.3.
uint8_t last_tx_seq_; // (AKA TxSeq)
// The sequence number we expect for the next packet sent _to_ us.
//
// We assume that the Extended Window Size option is _not_ enabled. In such
// cases, the sequence number is a 6-bit counter that wraps on overflow. See
// Core Spec v5.0, Vol 3, Part A, Secs 5.7 and 8.3.
uint8_t req_seqnum_;
// Filled by SetSingleRetransmit and cleared by UpdateAckSeq.
std::optional<SingleRetransmitRequest> single_request_;
// Filled by SetRangeRetransmit and cleared by UpdateAckSeq.
std::optional<RangeRetransmitRequest> range_request_;
// Set to AckSeq of the most recent frame retransmitted after sending a poll request but before
// receiving a poll response. Corresponds to the SrejActioned and SrejSaveReqSeq variables defined
// in Core Spec v5.0, Vol 3, Part A, Sec 8.6.5.3.
std::optional<uint8_t> retransmitted_single_during_poll_;
// True if we retransmitted a range of unacked data after sending a poll request but before
// receiving a poll response. Corresponds to the RejActioned variable defined in Core Spec v5.0
// Vol 3, Part A, Sec 8.6.5.3.
bool retransmitted_range_during_poll_;
uint8_t n_receiver_ready_polls_sent_;
bool remote_is_busy_;
std::list<PendingPdu> pending_pdus_;
async::TaskClosure receiver_ready_poll_task_;
async::TaskClosure monitor_task_;
fit::thread_checker thread_checker_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(EnhancedRetransmissionModeTxEngine);
};
} // namespace bt::l2cap::internal
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_ENHANCED_RETRANSMISSION_MODE_TX_ENGINE_H_