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 #pragma once
16 
17 #include "pw_bluetooth/hci_data.emb.h"
18 #include "pw_bluetooth/hci_events.emb.h"
19 #include "pw_bluetooth_proxy/internal/hci_transport.h"
20 #include "pw_bluetooth_proxy/internal/l2cap_aclu_signaling_channel.h"
21 #include "pw_bluetooth_proxy/internal/l2cap_leu_signaling_channel.h"
22 #include "pw_bluetooth_proxy/internal/l2cap_signaling_channel.h"
23 #include "pw_bluetooth_proxy/internal/logical_transport.h"
24 #include "pw_containers/vector.h"
25 #include "pw_result/result.h"
26 #include "pw_sync/lock_annotations.h"
27 #include "pw_sync/mutex.h"
28 
29 namespace pw::bluetooth::proxy {
30 
31 // Represents the Bluetooth ACL Data channel and tracks the Host<->Controller
32 // ACL data flow control.
33 //
34 // This currently only supports LE Packet-based Data Flow Control as defined in
35 // Core Spec v5.0, Vol 2, Part E, Section 4.1.1. Does not support sharing BR/EDR
36 // buffers.
37 class AclDataChannel {
38  public:
39   // Direction a packet is traveling on ACL transport.
40   enum class Direction {
41     kFromController,
42     kFromHost,
43 
44     // Must be last member
45     kMaxDirections,
46   };
47 
AclDataChannel(HciTransport & hci_transport,L2capChannelManager & l2cap_channel_manager,uint16_t le_acl_credits_to_reserve,uint16_t br_edr_acl_credits_to_reserve)48   AclDataChannel(HciTransport& hci_transport,
49                  L2capChannelManager& l2cap_channel_manager,
50                  uint16_t le_acl_credits_to_reserve,
51                  uint16_t br_edr_acl_credits_to_reserve)
52       : hci_transport_(hci_transport),
53         l2cap_channel_manager_(l2cap_channel_manager),
54         le_credits_(le_acl_credits_to_reserve),
55         br_edr_credits_(br_edr_acl_credits_to_reserve) {}
56 
57   AclDataChannel(const AclDataChannel&) = delete;
58   AclDataChannel& operator=(const AclDataChannel&) = delete;
59   AclDataChannel(AclDataChannel&&) = delete;
60   AclDataChannel& operator=(AclDataChannel&&) = delete;
61 
62   // Returns the max number of simultaneous LE ACL connections supported.
GetMaxNumLeAclConnections()63   static constexpr size_t GetMaxNumLeAclConnections() {
64     return kMaxConnections;
65   }
66 
67   // Revert to uninitialized state, clearing credit reservation and connections,
68   // but not the number of credits to reserve nor HCI transport.
69   void Reset();
70 
71   void ProcessReadBufferSizeCommandCompleteEvent(
72       emboss::ReadBufferSizeCommandCompleteEventWriter read_buffer_event);
73 
74   // Acquires LE ACL credits for proxy host use by removing the amount needed
75   // from the amount that is passed to the host.
ProcessLEReadBufferSizeCommandCompleteEvent(emboss::LEReadBufferSizeV1CommandCompleteEventWriter read_buffer_event)76   void ProcessLEReadBufferSizeCommandCompleteEvent(
77       emboss::LEReadBufferSizeV1CommandCompleteEventWriter read_buffer_event) {
78     ProcessSpecificLEReadBufferSizeCommandCompleteEvent(read_buffer_event);
79   }
80 
ProcessLEReadBufferSizeCommandCompleteEvent(emboss::LEReadBufferSizeV2CommandCompleteEventWriter read_buffer_event)81   void ProcessLEReadBufferSizeCommandCompleteEvent(
82       emboss::LEReadBufferSizeV2CommandCompleteEventWriter read_buffer_event) {
83     ProcessSpecificLEReadBufferSizeCommandCompleteEvent(read_buffer_event);
84   }
85 
86   // Remove completed packets from `nocp_event` as necessary to reclaim LE ACL
87   // credits that are associated with our credit-allocated connections.
88   void HandleNumberOfCompletedPacketsEvent(H4PacketWithHci&& h4_packet);
89 
90   // Reclaim any credits we have associated with the removed connection.
91   void HandleDisconnectionCompleteEvent(H4PacketWithHci&& h4_packet);
92 
93   // Create new tracked connection and pass on to host.
94   void HandleConnectionCompleteEvent(H4PacketWithHci&& h4_packet);
95 
96   // Create new tracked connection and pass on to host.
97   void HandleLeConnectionCompleteEvent(H4PacketWithHci&& h4_packet);
98 
99   // Create new tracked connection and pass on to host.
100   void HandleLeEnhancedConnectionCompleteV1Event(H4PacketWithHci&& h4_packet);
101 
102   // Create new tracked connection and pass on to host.
103   void HandleLeEnhancedConnectionCompleteV2Event(H4PacketWithHci&& h4_packet);
104 
105   /// Indicates whether the proxy has the capability of sending ACL packets.
106   /// Note that this indicates intention, so it can be true even if the proxy
107   /// has not yet or has been unable to reserve credits from the host.
108   bool HasSendAclCapability(AclTransportType transport) const;
109 
110   /// @deprecated Use HasSendAclCapability with transport parameter instead.
HasSendAclCapability()111   bool HasSendAclCapability() const {
112     return HasSendAclCapability(AclTransportType::kLe);
113   }
114 
115   // Returns the number of available ACL send credits for the proxy.
116   // Can be zero if the controller has not yet been initialized by the host.
117   uint16_t GetNumFreeAclPackets(AclTransportType transport) const;
118 
119   // Send an ACL data packet contained in an H4 packet to the controller.
120   //
121   // Returns PW_STATUS_UNAVAILABLE if no ACL send credits were available.
122   // Returns PW_STATUS_INVALID_ARGUMENT if ACL packet was ill-formed.
123   // Returns PW_NOT_FOUND if ACL connection does not exist.
124   pw::Status SendAcl(H4PacketWithH4&& h4_packet);
125 
126   // Register a new logical link on ACL logical transport.
127   //
128   // Returns PW_STATUS_OK if a connection was added.
129   // Returns PW_STATUS_ALREADY EXISTS if a connection already exists.
130   // Returns PW_STATUS_RESOURCE_EXHAUSTED if no space for additional connection.
131   pw::Status CreateAclConnection(uint16_t connection_handle,
132                                  AclTransportType transport);
133 
134   // Sets `is_receiving_fragmented_pdu` flag for connection in a given
135   // direction.
136   //
137   // Returns PW_STATUS_NOT_FOUND if connection does not exist.
138   // Returns PW_STATUS_FAILED_PRECONDITION if already receiving fragmented PDU.
139   pw::Status FragmentedPduStarted(Direction direction,
140                                   uint16_t connection_handle);
141 
142   // Returns `is_receiving_fragmented_pdu` flag for connection in a given
143   // direction.
144   //
145   // Returns PW_STATUS_NOT_FOUND if connection does not exist.
146   pw::Result<bool> IsReceivingFragmentedPdu(Direction direction,
147                                             uint16_t connection_handle);
148 
149   // Unsets `is_receiving_fragmented_pdu` flag for connection in a given
150   // direction.
151   //
152   // Returns PW_STATUS_NOT_FOUND if connection does not exist.
153   // Returns PW_STATUS_FAILED_PRECONDITION if not receiving fragmented PDU.
154   pw::Status FragmentedPduFinished(Direction direction,
155                                    uint16_t connection_handle);
156 
157   // Returns the signaling channel for this link if `connection_handle`
158   // references a tracked connection and `local_cid` matches its id.
159   L2capSignalingChannel* FindSignalingChannel(uint16_t connection_handle,
160                                               uint16_t local_cid);
161 
162  private:
163   // An active logical link on ACL logical transport.
164   // TODO: https://pwbug.dev/360929142 - Encapsulate all logic related to this
165   // within a new LogicalLinkManager class?
166   class AclConnection {
167    public:
AclConnection(AclTransportType transport,uint16_t connection_handle,uint16_t num_pending_packets,L2capChannelManager & l2cap_channel_manager)168     AclConnection(AclTransportType transport,
169                   uint16_t connection_handle,
170                   uint16_t num_pending_packets,
171                   L2capChannelManager& l2cap_channel_manager)
172         : transport_(transport),
173           connection_handle_(connection_handle),
174           num_pending_packets_(num_pending_packets),
175           leu_signaling_channel_(l2cap_channel_manager, connection_handle),
176           aclu_signaling_channel_(l2cap_channel_manager, connection_handle),
177           is_receiving_fragmented_pdu_{} {}
178 
179     AclConnection& operator=(AclConnection&& other) = default;
180 
connection_handle()181     uint16_t connection_handle() const { return connection_handle_; }
182 
num_pending_packets()183     uint16_t num_pending_packets() const { return num_pending_packets_; }
184 
transport()185     AclTransportType transport() const { return transport_; }
186 
set_num_pending_packets(uint16_t new_val)187     void set_num_pending_packets(uint16_t new_val) {
188       num_pending_packets_ = new_val;
189     }
190 
is_receiving_fragmented_pdu(Direction direction)191     bool is_receiving_fragmented_pdu(Direction direction) const {
192       return is_receiving_fragmented_pdu_[cpp23::to_underlying(direction)];
193     }
194 
set_is_receiving_fragmented_pdu(Direction direction,bool new_val)195     void set_is_receiving_fragmented_pdu(Direction direction, bool new_val) {
196       is_receiving_fragmented_pdu_[cpp23::to_underlying(direction)] = new_val;
197     }
198 
signaling_channel()199     L2capSignalingChannel* signaling_channel() {
200       if (transport_ == AclTransportType::kLe) {
201         return &leu_signaling_channel_;
202       } else {
203         return &aclu_signaling_channel_;
204       }
205     }
206 
207    private:
208     AclTransportType transport_;
209     uint16_t connection_handle_;
210     uint16_t num_pending_packets_;
211     L2capLeUSignalingChannel leu_signaling_channel_;
212     // TODO: https://pwbug.dev/379172336 - Create correct signaling channel
213     // type based on link type.
214     L2capAclUSignalingChannel aclu_signaling_channel_;
215 
216     // Set when a fragmented PDU is received. Indexed by Direction. Continuing
217     // fragments are dropped until the PDU has been consumed, then this is
218     // unset.
219     // TODO: https://pwbug.dev/365179076 - Support recombination.
220     std::array<bool, cpp23::to_underlying(Direction::kMaxDirections)>
221         is_receiving_fragmented_pdu_;
222   };
223 
224   class Credits {
225    public:
Credits(uint16_t to_reserve)226     explicit Credits(uint16_t to_reserve) : to_reserve_(to_reserve) {}
227 
228     void Reset();
229 
230     // Reserves credits from the controllers max and returns how many the host
231     // can use.
232     uint16_t Reserve(uint16_t controller_max);
233 
234     // Mark `num_credits` as pending.
235     //
236     // Returns Status::ResourceExhausted() if there aren't enough available
237     // credits.
238     Status MarkPending(uint16_t num_credits);
239 
240     // Return `num_credits` to available pool.
241     void MarkCompleted(uint16_t num_credits);
242 
Remaining()243     uint16_t Remaining() const { return proxy_max_ - proxy_pending_; }
Available()244     bool Available() const { return Remaining() > 0; }
245 
246     // If this class was initialized with some number of credits to reserve,
247     // return true.
HasSendCapability()248     bool HasSendCapability() const { return to_reserve_ > 0; }
249 
250    private:
251     const uint16_t to_reserve_;
252     // The local number of HCI ACL Data packets that we have reserved for
253     // this proxy host to use.
254     uint16_t proxy_max_ = 0;
255     // The number of HCI ACL Data packets that we have sent to the controller
256     // and have not yet completed.
257     uint16_t proxy_pending_ = 0;
258   };
259 
260   // Returns pointer to AclConnection with provided `connection_handle` in
261   // `active_acl_connections_`. Returns nullptr if no such connection exists.
262   AclConnection* FindAclConnection(uint16_t connection_handle)
263       PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
264 
265   Credits& LookupCredits(AclTransportType transport)
266       PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
267 
268   const Credits& LookupCredits(AclTransportType transport) const
269       PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
270 
271   void HandleLeConnectionCompleteEvent(uint16_t connection_handle,
272                                        emboss::StatusCode status);
273 
274   // Maximum number of simultaneous credit-allocated LE connections supported.
275   // TODO: https://pwbug.dev/349700888 - Make size configurable.
276   static constexpr size_t kMaxConnections = 10;
277 
278   // Reference to the transport owned by the host.
279   HciTransport& hci_transport_;
280 
281   // TODO: https://pwbug.dev/360929142 - Remove this circular dependency.
282   L2capChannelManager& l2cap_channel_manager_;
283 
284   // Credit allocation will happen inside a mutex since it crosses thread
285   // boundaries. The mutex also guards interactions with ACL connection objects.
286   mutable pw::sync::Mutex mutex_;
287 
288   Credits le_credits_ PW_GUARDED_BY(mutex_);
289   Credits br_edr_credits_ PW_GUARDED_BY(mutex_);
290 
291   // List of credit-allocated ACL connections.
292   pw::Vector<AclConnection, kMaxConnections> active_acl_connections_
293       PW_GUARDED_BY(mutex_);
294 
295   // Instantiated in acl_data_channel.cc for
296   // `emboss::LEReadBufferSizeV1CommandCompleteEventWriter` and
297   // `emboss::LEReadBufferSizeV1CommandCompleteEventWriter`.
298   template <class EventT>
299   void ProcessSpecificLEReadBufferSizeCommandCompleteEvent(
300       EventT read_buffer_event);
301 };
302 
303 }  // namespace pw::bluetooth::proxy
304