blob: 5832e0051c254e1cba60a924f8ee1877efafe9d0 [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/l2cap/bredr_signaling_channel.h"
#include <zircon/compiler.h>
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
namespace bt::l2cap::internal {
BrEdrSignalingChannel::BrEdrSignalingChannel(fbl::RefPtr<Channel> chan, hci::Connection::Role role)
: SignalingChannel(std::move(chan), role) {
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)"
ZX_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