// Copyright 2024 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_proxy/basic_l2cap_channel.h" #include "pw_bluetooth/emboss_util.h" #include "pw_bluetooth/hci_data.emb.h" #include "pw_bluetooth/l2cap_frames.emb.h" #include "pw_bluetooth_proxy/l2cap_channel_common.h" #include "pw_log/log.h" #include "pw_status/try.h" namespace pw::bluetooth::proxy { pw::Result BasicL2capChannel::Create( L2capChannelManager& l2cap_channel_manager, multibuf::MultiBufAllocator* rx_multibuf_allocator, uint16_t connection_handle, AclTransportType transport, uint16_t local_cid, uint16_t remote_cid, OptionalPayloadReceiveCallback&& payload_from_controller_fn, OptionalPayloadReceiveCallback&& payload_from_host_fn, ChannelEventCallback&& event_fn) { if (!AreValidParameters(/*connection_handle=*/connection_handle, /*local_cid=*/local_cid, /*remote_cid=*/remote_cid)) { return pw::Status::InvalidArgument(); } return BasicL2capChannel( l2cap_channel_manager, rx_multibuf_allocator, /*connection_handle=*/connection_handle, /*transport=*/transport, /*local_cid=*/local_cid, /*remote_cid=*/remote_cid, /*payload_from_controller_fn=*/std::move(payload_from_controller_fn), /*payload_from_host_fn=*/std::move(payload_from_host_fn), /*event_fn=*/std::move(event_fn)); } StatusWithMultiBuf BasicL2capChannel::Write(multibuf::MultiBuf&& payload) { if (!IsOkL2capDataLength(payload.size())) { PW_LOG_WARN("Payload (%zu bytes) is too large. So will not process.", payload.size()); return {Status::InvalidArgument(), std::move(payload)}; } return L2capChannel::Write(std::move(payload)); } std::optional BasicL2capChannel::GenerateNextTxPacket() { if (state() != State::kRunning || PayloadQueueEmpty()) { return std::nullopt; } ConstByteSpan payload = GetFrontPayloadSpan(); pw::Result result = PopulateTxL2capPacket(payload.size_bytes()); if (!result.ok()) { return std::nullopt; } H4PacketWithH4 h4_packet = std::move(result.value()); Result result2 = MakeEmbossWriter(h4_packet.GetHciSpan()); PW_CHECK(result2.ok()); emboss::AclDataFrameWriter acl = result2.value(); // At this point we assume we can return a PDU with the payload. PopFrontPayload(); emboss::BFrameWriter bframe = emboss::MakeBFrameView( acl.payload().BackingStorage().data(), acl.payload().SizeInBytes()); PW_CHECK(bframe.IsComplete()); PW_CHECK(TryToCopyToEmbossStruct(bframe.payload(), payload)); PW_CHECK(acl.Ok()); PW_CHECK(bframe.Ok()); return h4_packet; } BasicL2capChannel::BasicL2capChannel( L2capChannelManager& l2cap_channel_manager, multibuf::MultiBufAllocator* rx_multibuf_allocator, uint16_t connection_handle, AclTransportType transport, uint16_t local_cid, uint16_t remote_cid, OptionalPayloadReceiveCallback&& payload_from_controller_fn, OptionalPayloadReceiveCallback&& payload_from_host_fn, ChannelEventCallback&& event_fn) : L2capChannel( l2cap_channel_manager, rx_multibuf_allocator, /*connection_handle=*/connection_handle, /*transport=*/transport, /*local_cid=*/local_cid, /*remote_cid=*/remote_cid, /*payload_from_controller_fn=*/std::move(payload_from_controller_fn), /*payload_from_host_fn=*/std::move(payload_from_host_fn), /*event_fn=*/std::move(event_fn)) { PW_LOG_INFO("btproxy: BasicL2capChannel ctor"); } BasicL2capChannel::~BasicL2capChannel() { // Don't log dtor of moved-from channels. if (state() != State::kUndefined) { PW_LOG_INFO("btproxy: BasicL2capChannel dtor"); } } bool BasicL2capChannel::DoHandlePduFromController(pw::span bframe) { Result bframe_view = MakeEmbossWriter(bframe); if (!bframe_view.ok()) { // TODO: https://pwbug.dev/360929142 - Stop channel on error. PW_LOG_ERROR("(CID: 0x%X) Received invalid B-frame. So will drop.", local_cid()); return true; } return SendPayloadFromControllerToClient( span(bframe_view->payload().BackingStorage().data(), bframe_view->payload().SizeInBytes())); } bool BasicL2capChannel::HandlePduFromHost(pw::span bframe) { Result bframe_view = MakeEmbossWriter(bframe); if (!bframe_view.ok()) { // TODO: https://pwbug.dev/360929142 - Stop channel on error. PW_LOG_ERROR("(CID: 0x%X) Host transmitted invalid B-frame. So will drop.", local_cid()); return true; } return SendPayloadFromHostToClient( span(bframe_view->payload().BackingStorage().data(), bframe_view->payload().SizeInBytes())); } } // namespace pw::bluetooth::proxy