xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/acl_data_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/internal/acl_data_channel.h"
16 
17 #include <mutex>
18 
19 #include "lib/stdcompat/utility.h"
20 #include "pw_bluetooth/emboss_util.h"
21 #include "pw_bluetooth/hci_data.emb.h"
22 #include "pw_bluetooth_proxy/internal/l2cap_channel_manager.h"
23 #include "pw_containers/algorithm.h"  // IWYU pragma: keep
24 #include "pw_log/log.h"
25 #include "pw_status/status.h"
26 
27 namespace pw::bluetooth::proxy {
28 
Reset()29 void AclDataChannel::Reset() {
30   std::lock_guard lock(mutex_);
31   le_credits_.Reset();
32   br_edr_credits_.Reset();
33   active_acl_connections_.clear();
34 }
35 
Reset()36 void AclDataChannel::Credits::Reset() {
37   proxy_max_ = 0;
38   proxy_pending_ = 0;
39 }
40 
Reserve(uint16_t controller_max)41 uint16_t AclDataChannel::Credits::Reserve(uint16_t controller_max) {
42   PW_CHECK(proxy_max_ == 0,
43            "AclDataChannel is already initialized, but encountered another "
44            "ReadBufferSizeCommandCompleteEvent.");
45 
46   proxy_max_ = std::min(controller_max, to_reserve_);
47   const uint16_t host_max = controller_max - proxy_max_;
48 
49   PW_LOG_INFO(
50       "Bluetooth Proxy reserved %d ACL data credits. Passed %d on to host.",
51       proxy_max_,
52       host_max);
53 
54   if (proxy_max_ < to_reserve_) {
55     PW_LOG_ERROR(
56         "Only was able to reserve %d acl data credits rather than the "
57         "configured %d from the controller provided's data credits of %d. ",
58         proxy_max_,
59         to_reserve_,
60         controller_max);
61   }
62 
63   return host_max;
64 }
65 
MarkPending(uint16_t num_credits)66 Status AclDataChannel::Credits::MarkPending(uint16_t num_credits) {
67   if (num_credits > Available()) {
68     return Status::ResourceExhausted();
69   }
70 
71   proxy_pending_ += num_credits;
72 
73   return OkStatus();
74 }
75 
MarkCompleted(uint16_t num_credits)76 void AclDataChannel::Credits::MarkCompleted(uint16_t num_credits) {
77   if (num_credits > proxy_pending_) {
78     PW_LOG_ERROR("Tried to mark completed more packets than were pending.");
79     proxy_pending_ = 0;
80   } else {
81     proxy_pending_ -= num_credits;
82   }
83 }
84 
LookupCredits(AclTransportType transport)85 AclDataChannel::Credits& AclDataChannel::LookupCredits(
86     AclTransportType transport) {
87   switch (transport) {
88     case AclTransportType::kBrEdr:
89       return br_edr_credits_;
90     case AclTransportType::kLe:
91       return le_credits_;
92     default:
93       PW_CHECK(false, "Invalid transport type");
94   }
95 }
96 
LookupCredits(AclTransportType transport) const97 const AclDataChannel::Credits& AclDataChannel::LookupCredits(
98     AclTransportType transport) const {
99   switch (transport) {
100     case AclTransportType::kBrEdr:
101       return br_edr_credits_;
102     case AclTransportType::kLe:
103       return le_credits_;
104     default:
105       PW_CHECK(false, "Invalid transport type");
106   }
107 }
108 
ProcessReadBufferSizeCommandCompleteEvent(emboss::ReadBufferSizeCommandCompleteEventWriter read_buffer_event)109 void AclDataChannel::ProcessReadBufferSizeCommandCompleteEvent(
110     emboss::ReadBufferSizeCommandCompleteEventWriter read_buffer_event) {
111   {
112     std::lock_guard lock(mutex_);
113     const uint16_t controller_max =
114         read_buffer_event.total_num_acl_data_packets().Read();
115     const uint16_t host_max = br_edr_credits_.Reserve(controller_max);
116     read_buffer_event.total_num_acl_data_packets().Write(host_max);
117   }
118 
119   l2cap_channel_manager_.DrainWriteChannelQueues();
120 }
121 
122 template <class EventT>
ProcessSpecificLEReadBufferSizeCommandCompleteEvent(EventT read_buffer_event)123 void AclDataChannel::ProcessSpecificLEReadBufferSizeCommandCompleteEvent(
124     EventT read_buffer_event) {
125   {
126     std::lock_guard lock(mutex_);
127     const uint16_t controller_max =
128         read_buffer_event.total_num_le_acl_data_packets().Read();
129     // TODO: https://pwbug.dev/380316252 - Support shared buffers.
130     const uint16_t host_max = le_credits_.Reserve(controller_max);
131     read_buffer_event.total_num_le_acl_data_packets().Write(host_max);
132   }
133 
134   // Send packets that may have queued before we acquired any LE ACL credits.
135   l2cap_channel_manager_.DrainWriteChannelQueues();
136 }
137 
138 template void
139 AclDataChannel::ProcessSpecificLEReadBufferSizeCommandCompleteEvent<
140     emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
141     emboss::LEReadBufferSizeV1CommandCompleteEventWriter read_buffer_event);
142 
143 template void
144 AclDataChannel::ProcessSpecificLEReadBufferSizeCommandCompleteEvent<
145     emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
146     emboss::LEReadBufferSizeV2CommandCompleteEventWriter read_buffer_event);
147 
HandleNumberOfCompletedPacketsEvent(H4PacketWithHci && h4_packet)148 void AclDataChannel::HandleNumberOfCompletedPacketsEvent(
149     H4PacketWithHci&& h4_packet) {
150   Result<emboss::NumberOfCompletedPacketsEventWriter> nocp_event =
151       MakeEmbossWriter<emboss::NumberOfCompletedPacketsEventWriter>(
152           h4_packet.GetHciSpan());
153   if (!nocp_event.ok()) {
154     PW_LOG_ERROR(
155         "Buffer is too small for NUMBER_OF_COMPLETED_PACKETS event. So "
156         "will not process.");
157     hci_transport_.SendToHost(std::move(h4_packet));
158     return;
159   }
160 
161   bool should_send_to_host = false;
162   bool did_reclaim_credits = false;
163   {
164     std::lock_guard lock(mutex_);
165     for (uint8_t i = 0; i < nocp_event->num_handles().Read(); ++i) {
166       uint16_t handle = nocp_event->nocp_data()[i].connection_handle().Read();
167       uint16_t num_completed_packets =
168           nocp_event->nocp_data()[i].num_completed_packets().Read();
169 
170       if (num_completed_packets == 0) {
171         continue;
172       }
173 
174       AclConnection* connection_ptr = FindAclConnection(handle);
175       if (!connection_ptr) {
176         // Credits for connection we are not tracking, so should pass event on
177         // to host.
178         should_send_to_host = true;
179         continue;
180       }
181 
182       // Reclaim proxy's credits before event is forwarded to host
183       uint16_t num_pending_packets = connection_ptr->num_pending_packets();
184       uint16_t num_reclaimed =
185           std::min(num_completed_packets, num_pending_packets);
186 
187       if (num_reclaimed > 0) {
188         did_reclaim_credits = true;
189       }
190 
191       LookupCredits(connection_ptr->transport()).MarkCompleted(num_reclaimed);
192 
193       connection_ptr->set_num_pending_packets(num_pending_packets -
194                                               num_reclaimed);
195 
196       uint16_t credits_remaining = num_completed_packets - num_reclaimed;
197       nocp_event->nocp_data()[i].num_completed_packets().Write(
198           credits_remaining);
199       if (credits_remaining > 0) {
200         // Connection has credits remaining, so should past event on to host.
201         should_send_to_host = true;
202       }
203     }
204   }
205 
206   if (did_reclaim_credits) {
207     l2cap_channel_manager_.DrainWriteChannelQueues();
208   }
209   if (should_send_to_host) {
210     hci_transport_.SendToHost(std::move(h4_packet));
211   }
212 }
213 
HandleConnectionCompleteEvent(H4PacketWithHci && h4_packet)214 void AclDataChannel::HandleConnectionCompleteEvent(
215     H4PacketWithHci&& h4_packet) {
216   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
217   Result<emboss::ConnectionCompleteEventView> connection_complete_event =
218       MakeEmbossView<emboss::ConnectionCompleteEventView>(hci_buffer);
219   if (!connection_complete_event.ok()) {
220     hci_transport_.SendToHost(std::move(h4_packet));
221     return;
222   }
223 
224   if (connection_complete_event->status().Read() !=
225       emboss::StatusCode::SUCCESS) {
226     hci_transport_.SendToHost(std::move(h4_packet));
227     return;
228   }
229 
230   const uint16_t conn_handle =
231       connection_complete_event->connection_handle().Read();
232 
233   if (CreateAclConnection(conn_handle, AclTransportType::kBrEdr) ==
234       Status::ResourceExhausted()) {
235     PW_LOG_ERROR(
236         "Could not track connection like requested. Max connections "
237         "reached.");
238   }
239 
240   hci_transport_.SendToHost(std::move(h4_packet));
241 }
242 
HandleLeConnectionCompleteEvent(uint16_t connection_handle,emboss::StatusCode status)243 void AclDataChannel::HandleLeConnectionCompleteEvent(
244     uint16_t connection_handle, emboss::StatusCode status) {
245   if (status != emboss::StatusCode::SUCCESS) {
246     return;
247   }
248 
249   if (CreateAclConnection(connection_handle, AclTransportType::kLe) ==
250       Status::ResourceExhausted()) {
251     PW_LOG_ERROR(
252         "Could not track connection like requested. Max connections "
253         "reached.");
254   }
255 }
256 
HandleLeConnectionCompleteEvent(H4PacketWithHci && h4_packet)257 void AclDataChannel::HandleLeConnectionCompleteEvent(
258     H4PacketWithHci&& h4_packet) {
259   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
260   Result<emboss::LEConnectionCompleteSubeventView> event =
261       MakeEmbossView<emboss::LEConnectionCompleteSubeventView>(hci_buffer);
262   if (!event.ok()) {
263     hci_transport_.SendToHost(std::move(h4_packet));
264     return;
265   }
266 
267   HandleLeConnectionCompleteEvent(event->connection_handle().Read(),
268                                   event->status().Read());
269 
270   hci_transport_.SendToHost(std::move(h4_packet));
271 }
272 
HandleLeEnhancedConnectionCompleteV1Event(H4PacketWithHci && h4_packet)273 void AclDataChannel::HandleLeEnhancedConnectionCompleteV1Event(
274     H4PacketWithHci&& h4_packet) {
275   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
276   Result<emboss::LEEnhancedConnectionCompleteSubeventV1View> event =
277       MakeEmbossView<emboss::LEEnhancedConnectionCompleteSubeventV1View>(
278           hci_buffer);
279   if (!event.ok()) {
280     hci_transport_.SendToHost(std::move(h4_packet));
281     return;
282   }
283 
284   HandleLeConnectionCompleteEvent(event->connection_handle().Read(),
285                                   event->status().Read());
286 
287   hci_transport_.SendToHost(std::move(h4_packet));
288 }
289 
HandleLeEnhancedConnectionCompleteV2Event(H4PacketWithHci && h4_packet)290 void AclDataChannel::HandleLeEnhancedConnectionCompleteV2Event(
291     H4PacketWithHci&& h4_packet) {
292   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
293   Result<emboss::LEEnhancedConnectionCompleteSubeventV2View> event =
294       MakeEmbossView<emboss::LEEnhancedConnectionCompleteSubeventV2View>(
295           hci_buffer);
296   if (!event.ok()) {
297     hci_transport_.SendToHost(std::move(h4_packet));
298     return;
299   }
300 
301   HandleLeConnectionCompleteEvent(event->connection_handle().Read(),
302                                   event->status().Read());
303 
304   hci_transport_.SendToHost(std::move(h4_packet));
305 }
306 
HandleDisconnectionCompleteEvent(H4PacketWithHci && h4_packet)307 void AclDataChannel::HandleDisconnectionCompleteEvent(
308     H4PacketWithHci&& h4_packet) {
309   Result<emboss::DisconnectionCompleteEventView> dc_event =
310       MakeEmbossView<emboss::DisconnectionCompleteEventView>(
311           h4_packet.GetHciSpan());
312   if (!dc_event.ok()) {
313     PW_LOG_ERROR(
314         "Buffer is too small for DISCONNECTION_COMPLETE event. So will not "
315         "process.");
316     hci_transport_.SendToHost(std::move(h4_packet));
317     return;
318   }
319 
320   {
321     std::lock_guard lock(mutex_);
322     uint16_t conn_handle = dc_event->connection_handle().Read();
323 
324     AclConnection* connection_ptr = FindAclConnection(conn_handle);
325     if (!connection_ptr) {
326       hci_transport_.SendToHost(std::move(h4_packet));
327       return;
328     }
329 
330     emboss::StatusCode status = dc_event->status().Read();
331     if (status == emboss::StatusCode::SUCCESS) {
332       if (connection_ptr->num_pending_packets() > 0) {
333         PW_LOG_WARN(
334             "Proxy viewed disconnect (reason: %#.2hhx) for connection %#.4hx "
335             "with packets in flight. Releasing associated credits",
336             cpp23::to_underlying(dc_event->reason().Read()),
337             conn_handle);
338 
339         LookupCredits(connection_ptr->transport())
340             .MarkCompleted(connection_ptr->num_pending_packets());
341       }
342 
343       active_acl_connections_.erase(connection_ptr);
344     } else {
345       if (connection_ptr->num_pending_packets() > 0) {
346         PW_LOG_WARN(
347             "Proxy viewed failed disconnect (status: %#.2hhx) for connection "
348             "%#.4hx with packets in flight. Not releasing associated credits.",
349             cpp23::to_underlying(status),
350             conn_handle);
351       }
352     }
353   }
354   hci_transport_.SendToHost(std::move(h4_packet));
355 }
356 
HasSendAclCapability(AclTransportType transport) const357 bool AclDataChannel::HasSendAclCapability(AclTransportType transport) const {
358   std::lock_guard lock(mutex_);
359   return LookupCredits(transport).HasSendCapability();
360 }
361 
GetNumFreeAclPackets(AclTransportType transport) const362 uint16_t AclDataChannel::GetNumFreeAclPackets(
363     AclTransportType transport) const {
364   std::lock_guard lock(mutex_);
365   return LookupCredits(transport).Remaining();
366 }
367 
SendAcl(H4PacketWithH4 && h4_packet)368 pw::Status AclDataChannel::SendAcl(H4PacketWithH4&& h4_packet) {
369   std::lock_guard lock(mutex_);
370   Result<emboss::AclDataFrameHeaderView> acl_view =
371       MakeEmbossView<emboss::AclDataFrameHeaderView>(h4_packet.GetHciSpan());
372   if (!acl_view.ok()) {
373     PW_LOG_ERROR("An invalid ACL packet was provided. So will not send.");
374     return pw::Status::InvalidArgument();
375   }
376   uint16_t handle = acl_view->handle().Read();
377 
378   AclConnection* connection_ptr = FindAclConnection(handle);
379   if (!connection_ptr) {
380     PW_LOG_ERROR("Tried to send ACL packet on unregistered connection.");
381     return pw::Status::NotFound();
382   }
383 
384   if (const auto status =
385           LookupCredits(connection_ptr->transport()).MarkPending(1);
386       !status.ok()) {
387     PW_LOG_WARN("No ACL send credits available. So will not send.");
388     return pw::Status::Unavailable();
389   }
390 
391   connection_ptr->set_num_pending_packets(
392       connection_ptr->num_pending_packets() + 1);
393 
394   hci_transport_.SendToController(std::move(h4_packet));
395   return pw::OkStatus();
396 }
397 
CreateAclConnection(uint16_t connection_handle,AclTransportType transport)398 Status AclDataChannel::CreateAclConnection(uint16_t connection_handle,
399                                            AclTransportType transport) {
400   std::lock_guard lock(mutex_);
401   AclConnection* connection_it = FindAclConnection(connection_handle);
402   if (connection_it) {
403     return Status::AlreadyExists();
404   }
405   if (active_acl_connections_.full()) {
406     return Status::ResourceExhausted();
407   }
408   active_acl_connections_.emplace_back(transport,
409                                        /*connection_handle=*/connection_handle,
410                                        /*num_pending_packets=*/0,
411                                        l2cap_channel_manager_);
412   return OkStatus();
413 }
414 
FragmentedPduStarted(Direction direction,uint16_t connection_handle)415 pw::Status AclDataChannel::FragmentedPduStarted(Direction direction,
416                                                 uint16_t connection_handle) {
417   std::lock_guard lock(mutex_);
418   AclConnection* connection_ptr = FindAclConnection(connection_handle);
419   if (!connection_ptr) {
420     return Status::NotFound();
421   }
422   if (connection_ptr->is_receiving_fragmented_pdu(direction)) {
423     return Status::FailedPrecondition();
424   }
425   connection_ptr->set_is_receiving_fragmented_pdu(direction, true);
426   return OkStatus();
427 }
428 
IsReceivingFragmentedPdu(Direction direction,uint16_t connection_handle)429 pw::Result<bool> AclDataChannel::IsReceivingFragmentedPdu(
430     Direction direction, uint16_t connection_handle) {
431   std::lock_guard lock(mutex_);
432   AclConnection* connection_ptr = FindAclConnection(connection_handle);
433   if (!connection_ptr) {
434     return Status::NotFound();
435   }
436   return connection_ptr->is_receiving_fragmented_pdu(direction);
437 }
438 
FragmentedPduFinished(Direction direction,uint16_t connection_handle)439 pw::Status AclDataChannel::FragmentedPduFinished(Direction direction,
440                                                  uint16_t connection_handle) {
441   std::lock_guard lock(mutex_);
442   AclConnection* connection_ptr = FindAclConnection(connection_handle);
443   if (!connection_ptr) {
444     return Status::NotFound();
445   }
446   if (!connection_ptr->is_receiving_fragmented_pdu(direction)) {
447     return Status::FailedPrecondition();
448   }
449   connection_ptr->set_is_receiving_fragmented_pdu(direction, false);
450   return OkStatus();
451 }
452 
FindSignalingChannel(uint16_t connection_handle,uint16_t local_cid)453 L2capSignalingChannel* AclDataChannel::FindSignalingChannel(
454     uint16_t connection_handle, uint16_t local_cid) {
455   std::lock_guard lock(mutex_);
456 
457   AclConnection* connection_ptr = FindAclConnection(connection_handle);
458   if (!connection_ptr) {
459     return nullptr;
460   }
461 
462   if (local_cid == connection_ptr->signaling_channel()->local_cid()) {
463     return connection_ptr->signaling_channel();
464   }
465   return nullptr;
466 }
467 
FindAclConnection(uint16_t connection_handle)468 AclDataChannel::AclConnection* AclDataChannel::FindAclConnection(
469     uint16_t connection_handle) {
470   AclConnection* connection_it = containers::FindIf(
471       active_acl_connections_,
472       [&connection_handle](const AclConnection& connection) {
473         return connection.connection_handle() == connection_handle;
474       });
475   return connection_it == active_acl_connections_.end() ? nullptr
476                                                         : connection_it;
477 }
478 
479 }  // namespace pw::bluetooth::proxy
480