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