blob: bc2013d971ccebe4c064ecb651e381eb8a6bf371 [file]
// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_bluetooth_sapphire/internal/host/l2cap/bredr_signaling_channel.h"
#include <pw_assert/check.h>
#include <pw_bytes/endian.h>
#include "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,
pw::bluetooth_sapphire::LeaseProvider& wake_lease_provider)
: SignalingChannel(std::move(chan), role, dispatcher, wake_lease_provider) {
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 callback) {
return SendRequest(
kEchoRequest,
data,
[cb = std::move(callback)](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)"
PW_DCHECK(sdu);
if (sdu->size() < sizeof(CommandHeader)) {
bt_log(DEBUG, "l2cap-bredr", "sig: dropped malformed ACL signaling packet");
return;
}
WeakPtr<SignalingChannel> self = GetWeakPtr();
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 = pw::bytes::ConvertOrderFrom(
cpp20::endian::little, 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));
if (!self.is_alive()) {
return;
}
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