blob: 7d785fc504ca27ed65bfe37a10c2bf4e76d40423 [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.
#include "src/connectivity/bluetooth/core/bt-host/l2cap/enhanced_retransmission_mode_rx_engine.h"
namespace bt {
namespace l2cap {
namespace internal {
namespace {
template <typename T>
std::optional<T> TryCopyFromPdu(const PDU& pdu) {
if (pdu.length() < sizeof(T))
return std::nullopt;
StaticByteBuffer<sizeof(T)> buf;
pdu.Copy(&buf, 0, sizeof(T));
return buf.template As<T>();
}
std::variant<std::monostate, const SimpleInformationFrameHeader,
const SimpleStartOfSduFrameHeader, const SimpleSupervisoryFrame>
GetFrameHeaderFromPdu(const PDU& pdu) {
const auto control_field_opt = TryCopyFromPdu<EnhancedControlField>(pdu);
if (!control_field_opt) {
// TODO(BT-713): Add metric counting runt frames.
return std::monostate();
}
const auto& control_field = control_field_opt.value();
if (control_field.designates_supervisory_frame()) {
const auto frame_opt = TryCopyFromPdu<SimpleSupervisoryFrame>(pdu);
if (!frame_opt) {
// TODO(BT-713): Add metric counting runt S-frames.
return std::monostate();
}
return frame_opt.value();
}
if (control_field.designates_start_of_segmented_sdu()) {
const auto frame_opt = TryCopyFromPdu<SimpleStartOfSduFrameHeader>(pdu);
if (!frame_opt) {
// TODO(BT-713): Add metric counting runt Start-of-SDU frames.
return std::monostate();
}
return frame_opt.value();
}
const auto frame_opt = TryCopyFromPdu<SimpleInformationFrameHeader>(pdu);
if (!frame_opt) {
// TODO(BT-713): Add metric counting runt I-frames.
return std::monostate();
}
return frame_opt.value();
}
bool IsMpsValid(const PDU& pdu) {
// TODO(quiche): Check PDU's length against the MPS.
return true;
}
} // namespace
using Engine = EnhancedRetransmissionModeRxEngine;
Engine::EnhancedRetransmissionModeRxEngine(
SendBasicFrameCallback send_basic_frame_callback)
: next_seqnum_(0),
send_basic_frame_callback_(std::move(send_basic_frame_callback)) {}
ByteBufferPtr Engine::ProcessPdu(PDU pdu) {
// A note on validation (see Vol 3, Part A, 3.3.7):
//
// We skip step 1 (validation of the Channel ID), as a frame with an
// unrecognized Channel ID will not be delivered to us. (Various
// L2CAP_ChannelManagerTest test cases verify that LogicalLink directs frames
// to their proper channels.)
//
// We skip step 2 (validation of FCS), as we don't support FCS.
//
// Step 3 (size checking) is implemented in IsMpsValid(), and
// GetFrameHeaderFromPdu().
//
// TODO(quiche): Implement step 4/5 (Check SAR bits, close connection on
// error).
if (!IsMpsValid(pdu)) {
// TODO(quiche): Close connection.
// TODO(BT-713): Add metric counting oversized frames.
return nullptr;
}
auto header = GetFrameHeaderFromPdu(pdu);
return std::visit(
[this, pdu = std::move(pdu)](auto&& header) mutable {
return ProcessFrame(header, std::move(pdu));
},
header);
}
ByteBufferPtr Engine::ProcessFrame(const SimpleInformationFrameHeader header,
PDU pdu) {
if (header.tx_seq() != next_seqnum_) {
// TODO(quiche): Send REJ frame.
// TODO(quiche): Add histogram for |header.tx_seq() - next_seqnum_|. This
// will give us an upper bound on the potential benefit of sending SREJ
// frames.
return nullptr;
}
// TODO(quiche): check if the frame is within the permitted window.
if (header.designates_part_of_segmented_sdu()) {
// TODO(quiche): Implement validation and handling of segmented frames.
return nullptr;
}
AdvanceSeqNum();
SimpleReceiverReadyFrame ack_frame;
ack_frame.set_request_seq_num(next_seqnum_);
send_basic_frame_callback_(std::make_unique<DynamicByteBuffer>(
BufferView(&ack_frame, sizeof(ack_frame))));
const auto header_len = sizeof(header);
const auto payload_len = pdu.length() - header_len;
auto sdu = std::make_unique<DynamicByteBuffer>(payload_len);
pdu.Copy(sdu.get(), header_len, payload_len);
return sdu;
}
ByteBufferPtr Engine::ProcessFrame(const SimpleStartOfSduFrameHeader, PDU pdu) {
// TODO(quiche): Implement validation and handling of Start-of-SDU frames.
return nullptr;
}
ByteBufferPtr Engine::ProcessFrame(const SimpleSupervisoryFrame sframe,
PDU pdu) {
if (sframe.function() == SupervisoryFunction::ReceiverReady &&
sframe.is_poll_request()) {
// TODO(quiche): Propagate ReqSeq to the transmit engine.
// See Core Spec, v5, Vol 3, Part A, Table 8.6, "Recv RR(P=1)".
//
// Note, however, that there may be additional work to do if we're in the
// REJ_SENT state. See Core Spec, v5, Vol 3, Part A, Table 8.7, "Recv
// RR(P=1)".
SimpleReceiverReadyFrame poll_response;
poll_response.set_is_poll_response();
poll_response.set_request_seq_num(next_seqnum_);
send_basic_frame_callback_(std::make_unique<DynamicByteBuffer>(
BufferView(&poll_response, sizeof(poll_response))));
return nullptr;
}
// TODO(quiche): Implement handling of other S-frames.
return nullptr;
}
ByteBufferPtr Engine::ProcessFrame(std::monostate, PDU pdu) {
// TODO(quiche): Close connection.
return nullptr;
}
void Engine::AdvanceSeqNum() {
++next_seqnum_;
if (next_seqnum_ > EnhancedControlField::kMaxSeqNum) {
next_seqnum_ = 0;
}
}
} // namespace internal
} // namespace l2cap
} // namespace bt