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/rfcomm_channel.h"
16
17 #include <mutex>
18
19 #include "pw_assert/check.h"
20 #include "pw_bluetooth/emboss_util.h"
21 #include "pw_bluetooth/hci_data.emb.h"
22 #include "pw_bluetooth/l2cap_frames.emb.h"
23 #include "pw_bluetooth/rfcomm_frames.emb.h"
24 #include "pw_bluetooth_proxy/internal/logical_transport.h"
25 #include "pw_bluetooth_proxy/internal/rfcomm_fcs.h"
26 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
27 #include "pw_log/log.h"
28
29 namespace pw::bluetooth::proxy {
30
RfcommChannel(RfcommChannel && other)31 RfcommChannel::RfcommChannel(RfcommChannel&& other)
32 : L2capChannel(static_cast<RfcommChannel&&>(other)),
33 rx_config_(other.rx_config_),
34 tx_config_(other.tx_config_),
35 channel_number_(other.channel_number_),
36 payload_from_controller_fn_(
37 std::move(other.payload_from_controller_fn_)) {
38 {
39 std::lock_guard lock(rx_mutex_);
40 std::lock_guard other_lock(other.rx_mutex_);
41 rx_credits_ = other.rx_credits_;
42 }
43 {
44 std::lock_guard lock(tx_mutex_);
45 std::lock_guard other_lock(other.tx_mutex_);
46 tx_credits_ = other.tx_credits_;
47 }
48 }
49
50 namespace {
51
52 // We always encode credits.
53 constexpr size_t kCreditsFieldSize = 1;
54
55 } // namespace
56
Write(multibuf::MultiBuf && payload)57 StatusWithMultiBuf RfcommChannel::Write(multibuf::MultiBuf&& payload) {
58 if (payload.size() > tx_config_.max_information_length - kCreditsFieldSize) {
59 PW_LOG_WARN("Payload (%zu bytes) is too large. So will not process.",
60 payload.size());
61 return {Status::InvalidArgument(), std::move(payload)};
62 }
63
64 return L2capChannel::Write(std::move(payload));
65 }
66
GenerateNextTxPacket()67 std::optional<H4PacketWithH4> RfcommChannel::GenerateNextTxPacket() {
68 if (state() != State::kRunning || PayloadQueueEmpty()) {
69 return std::nullopt;
70 }
71
72 ConstByteSpan payload = GetFrontPayloadSpan();
73
74 constexpr size_t kMaxShortLength = 0x7f;
75
76 const bool uses_extended_length = payload.size() > kMaxShortLength;
77 const size_t length_extended_size = uses_extended_length ? 1 : 0;
78 const size_t frame_size = emboss::RfcommFrame::MinSizeInBytes() +
79 length_extended_size + kCreditsFieldSize +
80 payload.size();
81
82 // TODO: https://pwbug.dev/379337260 - Support fragmentation.
83 pw::Result<H4PacketWithH4> h4_result = PopulateTxL2capPacket(frame_size);
84 if (!h4_result.ok()) {
85 return std::nullopt;
86 }
87 H4PacketWithH4 h4_packet = std::move(*h4_result);
88
89 Result<emboss::AclDataFrameWriter> result2 =
90 MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan());
91 PW_CHECK(result2.ok());
92 emboss::AclDataFrameWriter acl = result2.value();
93
94 // At this point we assume we can return a PDU with the payload.
95 PopFrontPayload();
96
97 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
98 acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
99 PW_CHECK(bframe.IsComplete());
100
101 emboss::RfcommFrameWriter rfcomm = emboss::MakeRfcommFrameView(
102 bframe.payload().BackingStorage().data(), bframe.payload().SizeInBytes());
103 PW_CHECK(bframe.payload().SizeInBytes() >= frame_size);
104
105 rfcomm.extended_address().Write(true);
106 // TODO: https://pwbug.dev/378691959 - Sniff correct C/R/D from Multiplexer
107 // control commands on RFCOMM channel 0
108 rfcomm.command_response_direction().Write(
109 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_RESPONDER);
110 rfcomm.channel().Write(channel_number_);
111
112 // Poll/Final = 1 indicates Credits present.
113 rfcomm.control().Write(
114 emboss::RfcommFrameType::
115 UNNUMBERED_INFORMATION_WITH_HEADER_CHECK_AND_POLL_FINAL);
116 PW_CHECK(rfcomm.has_credits().ValueOrDefault());
117
118 if (!uses_extended_length) {
119 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
120 rfcomm.length().Write(payload.size());
121 } else {
122 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::EXTENDED);
123 rfcomm.length_extended().Write(payload.size());
124 }
125
126 {
127 std::lock_guard lock(rx_mutex_);
128 // TODO: https://pwbug.dev/379184978 - Refill remote side with credits they
129 // have sent. We assume our receiver can handle data without need for
130 // blocking. Revisit when adding downstream flow control to this API.
131 const uint8_t to_refill = rx_config_.credits - rx_credits_;
132 rfcomm.credits().Write(to_refill);
133 rx_credits_ = rx_config_.credits;
134 }
135
136 if (rfcomm.information().SizeInBytes() < payload.size()) {
137 return std::nullopt;
138 }
139 PW_CHECK(rfcomm.information().SizeInBytes() == payload.size());
140 PW_CHECK(TryToCopyToEmbossStruct(
141 /*emboss_dest=*/rfcomm.information(),
142 /*src=*/payload));
143
144 // UIH frame type:
145 // FCS should be calculated over address and control fields.
146 rfcomm.fcs().Write(RfcommFcs(rfcomm));
147
148 PW_CHECK(acl.Ok());
149 PW_CHECK(bframe.Ok());
150 PW_CHECK(rfcomm.Ok());
151
152 return h4_packet;
153 }
154
DequeuePacket()155 std::optional<H4PacketWithH4> RfcommChannel::DequeuePacket() {
156 std::lock_guard lock(tx_mutex_);
157 if (tx_credits_ == 0) {
158 return std::nullopt;
159 }
160
161 std::optional<H4PacketWithH4> maybe_packet = L2capChannel::DequeuePacket();
162 if (maybe_packet.has_value()) {
163 --tx_credits_;
164 }
165 return maybe_packet;
166 }
167
Create(L2capChannelManager & l2cap_channel_manager,multibuf::MultiBufAllocator & rx_multibuf_allocator,uint16_t connection_handle,Config rx_config,Config tx_config,uint8_t channel_number,Function<void (multibuf::MultiBuf && payload)> && payload_from_controller_fn,ChannelEventCallback && event_fn)168 Result<RfcommChannel> RfcommChannel::Create(
169 L2capChannelManager& l2cap_channel_manager,
170 multibuf::MultiBufAllocator& rx_multibuf_allocator,
171 uint16_t connection_handle,
172 Config rx_config,
173 Config tx_config,
174 uint8_t channel_number,
175 Function<void(multibuf::MultiBuf&& payload)>&& payload_from_controller_fn,
176 ChannelEventCallback&& event_fn) {
177 if (!AreValidParameters(/*connection_handle=*/connection_handle,
178 /*local_cid=*/rx_config.cid,
179 /*remote_cid=*/tx_config.cid)) {
180 return Status::InvalidArgument();
181 }
182
183 return RfcommChannel(l2cap_channel_manager,
184 rx_multibuf_allocator,
185 connection_handle,
186 rx_config,
187 tx_config,
188 channel_number,
189 std::move(payload_from_controller_fn),
190 std::move(event_fn));
191 }
192
DoHandlePduFromController(pw::span<uint8_t> l2cap_pdu)193 bool RfcommChannel::DoHandlePduFromController(pw::span<uint8_t> l2cap_pdu) {
194 if (state() != State::kRunning) {
195 PW_LOG_WARN("Received data on stopped channel, passing on to host.");
196 return false;
197 }
198
199 Result<emboss::BFrameView> bframe_view =
200 MakeEmbossView<emboss::BFrameView>(l2cap_pdu);
201 if (!bframe_view.ok()) {
202 PW_LOG_ERROR(
203 "(CID %#x) Buffer is too small for L2CAP B-frame, passing on to host.",
204 local_cid());
205 return false;
206 }
207
208 Result<emboss::RfcommFrameView> rfcomm_view =
209 MakeEmbossView<emboss::RfcommFrameView>(
210 bframe_view->payload().BackingStorage().data(),
211 bframe_view->payload().SizeInBytes());
212 if (!rfcomm_view.ok()) {
213 PW_LOG_ERROR("Unable to parse RFCOMM frame, passing on to host.");
214 return false;
215 }
216
217 if (rfcomm_view->channel().Read() == 0 || !rfcomm_view->uih().Read()) {
218 // Ignore control frames.
219 return false;
220 }
221
222 const uint8_t fcs = RfcommFcs(*rfcomm_view);
223 if (rfcomm_view->fcs().Read() != fcs) {
224 PW_LOG_ERROR("Bad checksum %02X (exp %02X), passing on to host.",
225 rfcomm_view->fcs().Read(),
226 fcs);
227 return false;
228 }
229
230 // TODO: https://pwbug.dev/378691959 - Validate channel, control, C/R,
231 // direction is what is expected.
232
233 if (rfcomm_view->channel().Read() != channel_number_) {
234 PW_LOG_WARN("RFCOMM data not for our channel %d (%d)",
235 rfcomm_view->channel().Read(),
236 channel_number_);
237 }
238
239 bool credits_previously_zero = false;
240 {
241 std::lock_guard lock(tx_mutex_);
242 credits_previously_zero = tx_credits_ == 0;
243 if (rfcomm_view->has_credits().ValueOrDefault()) {
244 tx_credits_ += rfcomm_view->credits().Read();
245 }
246 }
247
248 pw::span<uint8_t> information = pw::span(
249 const_cast<uint8_t*>(rfcomm_view->information().BackingStorage().data()),
250 rfcomm_view->information().SizeInBytes());
251
252 SendPayloadFromControllerToClient(information);
253
254 bool rx_needs_refill = false;
255 {
256 std::lock_guard lock(rx_mutex_);
257 if (rx_credits_ == 0) {
258 PW_LOG_ERROR("Received frame with no rx credits available.");
259 // TODO: https://pwbug.dev/379184978 - Consider dropping channel since
260 // this is invalid state.
261 } else {
262 --rx_credits_;
263 }
264 rx_needs_refill = rx_credits_ < kMinRxCredits;
265 }
266
267 if (rx_needs_refill) {
268 // Send credit update with empty payload to refresh remote credit count.
269 if (const auto status = Write(pw::multibuf::MultiBuf{}).status;
270 !status.ok()) {
271 PW_LOG_ERROR("Failed to send RFCOMM credits");
272 }
273 }
274
275 if (credits_previously_zero) {
276 ReportNewTxPacketsOrCredits();
277 }
278
279 return true;
280 }
281
SendPayloadFromControllerToClient(pw::span<uint8_t> payload)282 bool RfcommChannel::SendPayloadFromControllerToClient(
283 pw::span<uint8_t> payload) {
284 PW_CHECK(rx_multibuf_allocator());
285 std::optional<multibuf::MultiBuf> buffer =
286 rx_multibuf_allocator()->AllocateContiguous(payload.size());
287
288 if (!buffer) {
289 PW_LOG_ERROR(
290 "(CID %#x) Rx MultiBuf allocator out of memory. So stopping "
291 "channel "
292 "and reporting it needs to be closed.",
293 local_cid());
294 StopAndSendEvent(L2capChannelEvent::kRxOutOfMemory);
295 return true;
296 }
297
298 StatusWithSize status = buffer->CopyFrom(/*source=*/as_bytes(payload),
299 /*position=*/0);
300 PW_CHECK_OK(status);
301
302 if (payload_from_controller_fn_) {
303 payload_from_controller_fn_(std::move(*buffer));
304 }
305
306 return true;
307 }
308
HandlePduFromHost(pw::span<uint8_t>)309 bool RfcommChannel::HandlePduFromHost(pw::span<uint8_t>) { return false; }
310
RfcommChannel(L2capChannelManager & l2cap_channel_manager,multibuf::MultiBufAllocator & rx_multibuf_allocator,uint16_t connection_handle,Config rx_config,Config tx_config,uint8_t channel_number,Function<void (multibuf::MultiBuf && payload)> && payload_from_controller_fn,ChannelEventCallback && event_fn)311 RfcommChannel::RfcommChannel(
312 L2capChannelManager& l2cap_channel_manager,
313 multibuf::MultiBufAllocator& rx_multibuf_allocator,
314 uint16_t connection_handle,
315 Config rx_config,
316 Config tx_config,
317 uint8_t channel_number,
318 Function<void(multibuf::MultiBuf&& payload)>&& payload_from_controller_fn,
319 ChannelEventCallback&& event_fn)
320 : L2capChannel(l2cap_channel_manager,
321 &rx_multibuf_allocator,
322 /*connection_handle=*/connection_handle,
323 /*transport=*/AclTransportType::kBrEdr,
324 /*local_cid=*/rx_config.cid,
325 /*remote_cid=*/tx_config.cid,
326 /*payload_from_controller_fn=*/nullptr,
327 /*payload_from_host_fn=*/nullptr,
328 /*event_fn=*/std::move(event_fn)),
329 rx_config_(rx_config),
330 tx_config_(tx_config),
331 channel_number_(channel_number),
332 rx_credits_(rx_config.credits),
333 tx_credits_(tx_config.credits),
334 payload_from_controller_fn_(std::move(payload_from_controller_fn)) {
335 PW_LOG_INFO(
336 "btproxy: RfcommChannel ctor - channel_number_: %u, rx_credits_: %u, "
337 "tx_credits_: %u",
338 channel_number_,
339 rx_credits_,
340 tx_credits_);
341 }
342
~RfcommChannel()343 RfcommChannel::~RfcommChannel() {
344 // Don't log dtor of moved-from channels.
345 if (state() != State::kUndefined) {
346 PW_LOG_INFO("btproxy: RfcommChannel dtor - channel_number_: %u",
347 channel_number_);
348 }
349 }
350
351 } // namespace pw::bluetooth::proxy
352