blob: a2d58a6871b6d7da97bdfa8d8cb5b13d13821f61 [file] [log] [blame]
// Copyright 2018 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/public/pw_bluetooth_sapphire/internal/host/l2cap/bredr_signaling_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
namespace bt::l2cap::internal {
BrEdrSignalingChannel::BrEdrSignalingChannel(
Channel::WeakPtr chan,
pw::bluetooth::emboss::ConnectionRole role,
pw::async::Dispatcher& dispatcher)
: SignalingChannel(std::move(chan), role, dispatcher) {
set_mtu(kDefaultMTU);
// Add default handler for incoming Echo Request commands.
ServeRequest(kEchoRequest,
[](const ByteBuffer& req_payload, Responder* responder) {
responder->Send(req_payload);
});
}
bool BrEdrSignalingChannel::TestLink(const ByteBuffer& data, DataCallback cb) {
return SendRequest(
kEchoRequest,
data,
[cb = std::move(cb)](Status status, const ByteBuffer& rsp_payload) {
if (status == Status::kSuccess) {
cb(rsp_payload);
} else {
cb(BufferView());
}
return ResponseHandlerAction::kCompleteOutboundTransaction;
});
}
void BrEdrSignalingChannel::DecodeRxUnit(ByteBufferPtr sdu,
const SignalingPacketHandler& cb) {
// "Multiple commands may be sent in a single C-frame over Fixed Channel CID
// 0x0001 (ACL-U) (v5.0, Vol 3, Part A, Section 4)"
BT_DEBUG_ASSERT(sdu);
if (sdu->size() < sizeof(CommandHeader)) {
bt_log(DEBUG, "l2cap-bredr", "sig: dropped malformed ACL signaling packet");
return;
}
size_t sdu_offset = 0;
while (sdu_offset + sizeof(CommandHeader) <= sdu->size()) {
const auto header_data = sdu->view(sdu_offset, sizeof(CommandHeader));
SignalingPacket packet(&header_data);
uint16_t expected_payload_length = le16toh(packet.header().length);
size_t remaining_sdu_length =
sdu->size() - sdu_offset - sizeof(CommandHeader);
if (remaining_sdu_length < expected_payload_length) {
bt_log(DEBUG,
"l2cap-bredr",
"sig: expected more bytes (%zu < %u); drop",
remaining_sdu_length,
expected_payload_length);
SendCommandReject(
packet.header().id, RejectReason::kNotUnderstood, BufferView());
return;
}
const auto packet_data =
sdu->view(sdu_offset, sizeof(CommandHeader) + expected_payload_length);
cb(SignalingPacket(&packet_data, expected_payload_length));
sdu_offset += packet_data.size();
}
if (sdu_offset != sdu->size()) {
bt_log(DEBUG,
"l2cap-bredr",
"sig: incomplete packet header "
"(expected: %zu, left: %zu)",
sizeof(CommandHeader),
sdu->size() - sdu_offset);
}
}
bool BrEdrSignalingChannel::IsSupportedResponse(CommandCode code) const {
switch (code) {
case kCommandRejectCode:
case kConnectionResponse:
case kConfigurationResponse:
case kDisconnectionResponse:
case kEchoResponse:
case kInformationResponse:
return true;
}
// Other response-type commands are for AMP/LE and are not supported.
return false;
}
} // namespace bt::l2cap::internal