• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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