// 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 #include #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) : 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 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; } 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)); 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