xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/rfcomm_channel.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/l2cap_write_channel.h"
25 #include "pw_bluetooth_proxy/internal/rfcomm_fcs.h"
26 #include "pw_log/log.h"
27 #include "pw_status/try.h"
28 
29 namespace pw::bluetooth::proxy {
30 
RfcommChannel(RfcommChannel && other)31 RfcommChannel::RfcommChannel(RfcommChannel&& other)
32     : L2capWriteChannel(std::move(static_cast<L2capWriteChannel&>(other))),
33       L2capReadChannel(std::move(static_cast<L2capReadChannel&>(other))),
34       rx_config_(other.rx_config_),
35       tx_config_(other.tx_config_),
36       channel_number_(other.channel_number_) {
37   std::lock_guard lock(mutex_);
38   std::lock_guard other_lock(other.mutex_);
39   rx_credits_ = other.rx_credits_;
40   tx_credits_ = other.tx_credits_;
41   state_ = other.state_;
42   other.state_ = State::kStopped;
43 }
44 
Write(pw::span<const uint8_t> payload)45 pw::Status RfcommChannel::Write(pw::span<const uint8_t> payload) {
46   if (state_ == State::kStopped) {
47     return Status::FailedPrecondition();
48   }
49 
50   // We always encode credits.
51   const size_t kCreditsFieldSize = 1;
52 
53   if (payload.size() > tx_config_.max_information_length - kCreditsFieldSize) {
54     return Status::InvalidArgument();
55   }
56 
57   constexpr size_t kMaxShortLength = 0x7f;
58 
59   const bool uses_extended_length = payload.size() > kMaxShortLength;
60   const size_t length_extended_size = uses_extended_length ? 1 : 0;
61   const size_t frame_size = emboss::RfcommFrame::MinSizeInBytes() +
62                             length_extended_size + kCreditsFieldSize +
63                             payload.size();
64 
65   // TODO: https://pwbug.dev/365179076 - Support fragmentation.
66   pw::Result<H4PacketWithH4> h4_result = PopulateTxL2capPacket(frame_size);
67   if (!h4_result.ok()) {
68     return h4_result.status();
69   }
70   H4PacketWithH4 h4_packet = std::move(*h4_result);
71 
72   PW_TRY_ASSIGN(
73       auto acl,
74       MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan()));
75   auto bframe = emboss::MakeBFrameView(acl.payload().BackingStorage().data(),
76                                        acl.payload().SizeInBytes());
77   PW_TRY_ASSIGN(auto rfcomm,
78                 MakeEmbossWriter<emboss::RfcommFrameWriter>(
79                     bframe.payload().BackingStorage().data(),
80                     bframe.payload().SizeInBytes()));
81 
82   rfcomm.extended_address().Write(true);
83   // TODO: https://pwbug.dev/378691959 - Sniff correct C/R/D from Multiplexer
84   // control commands on RFCOMM channel 0
85   rfcomm.command_response_direction().Write(
86       emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_RESPONDER);
87   rfcomm.channel().Write(channel_number_);
88 
89   // Poll/Final = 1 indicates Credits present.
90   rfcomm.control().Write(
91       emboss::RfcommFrameType::
92           UNNUMBERED_INFORMATION_WITH_HEADER_CHECK_AND_POLL_FINAL);
93   PW_CHECK(rfcomm.has_credits().ValueOrDefault());
94 
95   if (!uses_extended_length) {
96     rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
97     rfcomm.length().Write(payload.size());
98   } else {
99     rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::EXTENDED);
100     rfcomm.length_extended().Write(payload.size());
101   }
102 
103   {
104     std::lock_guard lock(mutex_);
105     // TODO: https://pwbug.dev/379184978 - Refill remote side with credits they
106     // have sent. We assume our receiver can handle data without need for
107     // blocking. Revisit when adding downstream flow control to this API.
108     const uint8_t to_refill = rx_config_.credits - rx_credits_;
109     rfcomm.credits().Write(to_refill);
110     rx_credits_ = rx_config_.credits;
111   }
112 
113   if (rfcomm.information().SizeInBytes() < payload.size()) {
114     return Status::ResourceExhausted();
115   }
116   PW_CHECK(rfcomm.information().SizeInBytes() == payload.size());
117   std::memcpy(rfcomm.information().BackingStorage().data(),
118               payload.data(),
119               payload.size());
120 
121   // UIH frame type:
122   //   FCS should be calculated over address and control fields.
123   rfcomm.fcs().Write(RfcommFcs(rfcomm));
124 
125   // TODO: https://pwbug.dev/379184978 - Support legacy non-credit based flow
126   // control.
127 
128   return QueuePacket(std::move(h4_packet));
129 }
130 
DequeuePacket()131 std::optional<H4PacketWithH4> RfcommChannel::DequeuePacket() {
132   std::lock_guard lock(mutex_);
133   if (tx_credits_ == 0) {
134     return std::nullopt;
135   }
136 
137   std::optional<H4PacketWithH4> maybe_packet =
138       L2capWriteChannel::DequeuePacket();
139   if (maybe_packet.has_value()) {
140     --tx_credits_;
141   }
142   return maybe_packet;
143 }
144 
Create(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,Config rx_config,Config tx_config,uint8_t channel_number,pw::Function<void (pw::span<uint8_t> payload)> && receive_fn)145 Result<RfcommChannel> RfcommChannel::Create(
146     L2capChannelManager& l2cap_channel_manager,
147     uint16_t connection_handle,
148     Config rx_config,
149     Config tx_config,
150     uint8_t channel_number,
151     pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn) {
152   if (!L2capWriteChannel::AreValidParameters(connection_handle,
153                                              tx_config.cid) ||
154       !L2capReadChannel::AreValidParameters(connection_handle, rx_config.cid)) {
155     return Status::InvalidArgument();
156   }
157 
158   return RfcommChannel(l2cap_channel_manager,
159                        connection_handle,
160                        rx_config,
161                        tx_config,
162                        channel_number,
163                        std::move(receive_fn));
164 }
165 
HandlePduFromController(pw::span<uint8_t> l2cap_pdu)166 bool RfcommChannel::HandlePduFromController(pw::span<uint8_t> l2cap_pdu) {
167   if (state_ == State::kStopped) {
168     PW_LOG_WARN("Received data on stopped channel, passing on to host.");
169     return false;
170   }
171 
172   Result<emboss::BFrameView> bframe_view =
173       MakeEmbossView<emboss::BFrameView>(l2cap_pdu);
174   if (!bframe_view.ok()) {
175     PW_LOG_ERROR(
176         "(CID 0x%X) Buffer is too small for L2CAP B-frame, passing on to host.",
177         local_cid());
178     return false;
179   }
180 
181   Result<emboss::RfcommFrameView> rfcomm_view =
182       MakeEmbossView<emboss::RfcommFrameView>(
183           bframe_view->payload().BackingStorage().data(),
184           bframe_view->payload().SizeInBytes());
185   if (!rfcomm_view.ok()) {
186     PW_LOG_ERROR("Unable to parse RFCOMM frame, passing on to host.");
187     return false;
188   }
189 
190   if (rfcomm_view->channel().Read() == 0 || !rfcomm_view->uih().Read()) {
191     // Ignore control frames.
192     return false;
193   }
194 
195   const uint8_t fcs = RfcommFcs(*rfcomm_view);
196   if (rfcomm_view->fcs().Read() != fcs) {
197     PW_LOG_ERROR("Bad checksum %02X (exp %02X), passing on to host.",
198                  rfcomm_view->fcs().Read(),
199                  fcs);
200     return false;
201   }
202 
203   // TODO: https://pwbug.dev/378691959 - Validate channel, control, C/R,
204   // direction is what is expected.
205 
206   if (rfcomm_view->channel().Read() != channel_number_) {
207     PW_LOG_WARN("RFCOMM data not for our channel %d (%d)",
208                 rfcomm_view->channel().Read(),
209                 channel_number_);
210   }
211 
212   bool credits_previously_zero = false;
213   {
214     std::lock_guard lock(mutex_);
215     credits_previously_zero = tx_credits_ == 0;
216     if (rfcomm_view->has_credits().ValueOrDefault()) {
217       tx_credits_ += rfcomm_view->credits().Read();
218     }
219   }
220 
221   pw::span<uint8_t> information = pw::span(
222       const_cast<uint8_t*>(rfcomm_view->information().BackingStorage().data()),
223       rfcomm_view->information().SizeInBytes());
224 
225   SendPayloadFromControllerToClient(information);
226 
227   bool rx_needs_refill = false;
228   {
229     std::lock_guard lock(mutex_);
230     if (rx_credits_ == 0) {
231       PW_LOG_ERROR("Received frame with no rx credits available.");
232       // TODO: https://pwbug.dev/379184978 - Consider dropping channel since
233       // this is invalid state.
234     } else {
235       --rx_credits_;
236     }
237     rx_needs_refill = rx_credits_ < kMinRxCredits;
238   }
239 
240   if (rx_needs_refill) {
241     // Send credit update with empty payload to refresh remote credit count.
242     if (const auto status = Write({}); !status.ok()) {
243       PW_LOG_ERROR("Failed to send RFCOMM credits");
244     }
245   }
246 
247   if (credits_previously_zero) {
248     ReportPacketsMayBeReadyToSend();
249   }
250 
251   return true;
252 }
253 
HandlePduFromHost(pw::span<uint8_t>)254 bool RfcommChannel::HandlePduFromHost(pw::span<uint8_t>) { return false; }
255 
RfcommChannel(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,Config rx_config,Config tx_config,uint8_t channel_number,pw::Function<void (pw::span<uint8_t> payload)> && receive_fn)256 RfcommChannel::RfcommChannel(
257     L2capChannelManager& l2cap_channel_manager,
258     uint16_t connection_handle,
259     Config rx_config,
260     Config tx_config,
261     uint8_t channel_number,
262     pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn)
263     : L2capWriteChannel(l2cap_channel_manager,
264                         connection_handle,
265                         AclTransportType::kBrEdr,
266                         tx_config.cid),
267       L2capReadChannel(l2cap_channel_manager,
268                        std::move(receive_fn),
269                        connection_handle,
270                        rx_config.cid),
271       rx_config_(rx_config),
272       tx_config_(tx_config),
273       channel_number_(channel_number),
274       rx_credits_(rx_config.credits),
275       tx_credits_(tx_config.credits),
276       state_(State::kStarted) {}
277 
OnFragmentedPduReceived()278 void RfcommChannel::OnFragmentedPduReceived() {
279   PW_LOG_ERROR(
280       "(CID 0x%X) Fragmented L2CAP frame received (which is not yet "
281       "supported).",
282       local_cid());
283 }
284 
285 }  // namespace pw::bluetooth::proxy
286