1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_proxy/basic_l2cap_channel.h"
16
17 #include "pw_bluetooth/emboss_util.h"
18 #include "pw_bluetooth/hci_data.emb.h"
19 #include "pw_bluetooth/l2cap_frames.emb.h"
20 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
21 #include "pw_log/log.h"
22 #include "pw_status/try.h"
23
24 namespace pw::bluetooth::proxy {
25
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)26 pw::Result<BasicL2capChannel> BasicL2capChannel::Create(
27 L2capChannelManager& l2cap_channel_manager,
28 multibuf::MultiBufAllocator* rx_multibuf_allocator,
29 uint16_t connection_handle,
30 AclTransportType transport,
31 uint16_t local_cid,
32 uint16_t remote_cid,
33 OptionalPayloadReceiveCallback&& payload_from_controller_fn,
34 OptionalPayloadReceiveCallback&& payload_from_host_fn,
35 ChannelEventCallback&& event_fn) {
36 if (!AreValidParameters(/*connection_handle=*/connection_handle,
37 /*local_cid=*/local_cid,
38 /*remote_cid=*/remote_cid)) {
39 return pw::Status::InvalidArgument();
40 }
41
42 return BasicL2capChannel(
43 l2cap_channel_manager,
44 rx_multibuf_allocator,
45 /*connection_handle=*/connection_handle,
46 /*transport=*/transport,
47 /*local_cid=*/local_cid,
48 /*remote_cid=*/remote_cid,
49 /*payload_from_controller_fn=*/std::move(payload_from_controller_fn),
50 /*payload_from_host_fn=*/std::move(payload_from_host_fn),
51 /*event_fn=*/std::move(event_fn));
52 }
53
Write(multibuf::MultiBuf && payload)54 StatusWithMultiBuf BasicL2capChannel::Write(multibuf::MultiBuf&& payload) {
55 if (!IsOkL2capDataLength(payload.size())) {
56 PW_LOG_WARN("Payload (%zu bytes) is too large. So will not process.",
57 payload.size());
58 return {Status::InvalidArgument(), std::move(payload)};
59 }
60
61 return L2capChannel::Write(std::move(payload));
62 }
63
GenerateNextTxPacket()64 std::optional<H4PacketWithH4> BasicL2capChannel::GenerateNextTxPacket() {
65 if (state() != State::kRunning || PayloadQueueEmpty()) {
66 return std::nullopt;
67 }
68
69 ConstByteSpan payload = GetFrontPayloadSpan();
70
71 pw::Result<H4PacketWithH4> result =
72 PopulateTxL2capPacket(payload.size_bytes());
73 if (!result.ok()) {
74 return std::nullopt;
75 }
76 H4PacketWithH4 h4_packet = std::move(result.value());
77
78 Result<emboss::AclDataFrameWriter> result2 =
79 MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan());
80 PW_CHECK(result2.ok());
81 emboss::AclDataFrameWriter acl = result2.value();
82
83 // At this point we assume we can return a PDU with the payload.
84 PopFrontPayload();
85
86 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
87 acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
88 PW_CHECK(bframe.IsComplete());
89
90 PW_CHECK(TryToCopyToEmbossStruct(bframe.payload(), payload));
91
92 PW_CHECK(acl.Ok());
93 PW_CHECK(bframe.Ok());
94
95 return h4_packet;
96 }
97
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)98 BasicL2capChannel::BasicL2capChannel(
99 L2capChannelManager& l2cap_channel_manager,
100 multibuf::MultiBufAllocator* rx_multibuf_allocator,
101 uint16_t connection_handle,
102 AclTransportType transport,
103 uint16_t local_cid,
104 uint16_t remote_cid,
105 OptionalPayloadReceiveCallback&& payload_from_controller_fn,
106 OptionalPayloadReceiveCallback&& payload_from_host_fn,
107 ChannelEventCallback&& event_fn)
108 : L2capChannel(
109 l2cap_channel_manager,
110 rx_multibuf_allocator,
111 /*connection_handle=*/connection_handle,
112 /*transport=*/transport,
113 /*local_cid=*/local_cid,
114 /*remote_cid=*/remote_cid,
115 /*payload_from_controller_fn=*/std::move(payload_from_controller_fn),
116 /*payload_from_host_fn=*/std::move(payload_from_host_fn),
117 /*event_fn=*/std::move(event_fn)) {
118 PW_LOG_INFO("btproxy: BasicL2capChannel ctor");
119 }
120
~BasicL2capChannel()121 BasicL2capChannel::~BasicL2capChannel() {
122 // Don't log dtor of moved-from channels.
123 if (state() != State::kUndefined) {
124 PW_LOG_INFO("btproxy: BasicL2capChannel dtor");
125 }
126 }
127
DoHandlePduFromController(pw::span<uint8_t> bframe)128 bool BasicL2capChannel::DoHandlePduFromController(pw::span<uint8_t> bframe) {
129 Result<emboss::BFrameWriter> bframe_view =
130 MakeEmbossWriter<emboss::BFrameWriter>(bframe);
131
132 if (!bframe_view.ok()) {
133 // TODO: https://pwbug.dev/360929142 - Stop channel on error.
134 PW_LOG_ERROR("(CID: 0x%X) Received invalid B-frame. So will drop.",
135 local_cid());
136 return true;
137 }
138
139 return SendPayloadFromControllerToClient(
140 span(bframe_view->payload().BackingStorage().data(),
141 bframe_view->payload().SizeInBytes()));
142 }
143
HandlePduFromHost(pw::span<uint8_t> bframe)144 bool BasicL2capChannel::HandlePduFromHost(pw::span<uint8_t> bframe) {
145 Result<emboss::BFrameWriter> bframe_view =
146 MakeEmbossWriter<emboss::BFrameWriter>(bframe);
147
148 if (!bframe_view.ok()) {
149 // TODO: https://pwbug.dev/360929142 - Stop channel on error.
150 PW_LOG_ERROR("(CID: 0x%X) Host transmitted invalid B-frame. So will drop.",
151 local_cid());
152 return true;
153 }
154
155 return SendPayloadFromHostToClient(
156 span(bframe_view->payload().BackingStorage().data(),
157 bframe_view->payload().SizeInBytes()));
158 }
159
160 } // namespace pw::bluetooth::proxy
161