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 <optional> 18 19 #include "pw_bluetooth/hci_data.emb.h" 20 #include "pw_bluetooth/hci_events.emb.h" 21 #include "pw_bluetooth_proxy/internal/hci_transport.h" 22 #include "pw_bluetooth_proxy/internal/l2cap_aclu_signaling_channel.h" 23 #include "pw_bluetooth_proxy/internal/l2cap_leu_signaling_channel.h" 24 #include "pw_bluetooth_proxy/internal/l2cap_signaling_channel.h" 25 #include "pw_bluetooth_proxy/internal/logical_transport.h" 26 #include "pw_bluetooth_proxy/internal/multibuf_writer.h" 27 #include "pw_containers/vector.h" 28 #include "pw_multibuf/allocator.h" 29 #include "pw_multibuf/multibuf.h" 30 #include "pw_result/result.h" 31 #include "pw_sync/lock_annotations.h" 32 #include "pw_sync/mutex.h" 33 34 namespace pw::bluetooth::proxy { 35 36 // Represents the Bluetooth ACL Data channel and tracks the Host<->Controller 37 // ACL data flow control. 38 // 39 // This currently only supports LE Packet-based Data Flow Control as defined in 40 // Core Spec v5.0, Vol 2, Part E, Section 4.1.1. Does not support sharing BR/EDR 41 // buffers. 42 class AclDataChannel { 43 public: 44 // Direction a packet is traveling on ACL transport. 45 enum class Direction : bool { 46 kFromController, 47 kFromHost, 48 }; 49 // Must match the number of Direction enumerators. 50 static constexpr size_t kNumDirections = 2; 51 52 static const char* ToString(Direction direction); 53 54 // Used to `SendAcl` packets. 55 class SendCredit { 56 public: 57 friend class AclDataChannel; 58 59 SendCredit(const SendCredit&) = delete; 60 SendCredit& operator=(const SendCredit&) = delete; 61 // Move-only 62 SendCredit(SendCredit&& other); 63 SendCredit& operator=(SendCredit&& other); 64 65 ~SendCredit(); 66 67 private: 68 // Dispensed via `AclDataChannel::ReserveSendCredit()`. 69 SendCredit(AclTransportType transport, 70 Function<void(AclTransportType transport)>&& relinquish_fn); 71 72 // Indicate that credits has been used for Tx. 73 void MarkUsed(); 74 75 AclTransportType transport_; 76 // If `this` was not used or moved and is destructed, `relinquish_fn_` is 77 // called to replenish the credit that was subtracted from `AclDataChannel`. 78 Function<void(AclTransportType transport)> relinquish_fn_; 79 }; 80 AclDataChannel(HciTransport & hci_transport,L2capChannelManager & l2cap_channel_manager,uint16_t le_acl_credits_to_reserve,uint16_t br_edr_acl_credits_to_reserve)81 AclDataChannel(HciTransport& hci_transport, 82 L2capChannelManager& l2cap_channel_manager, 83 uint16_t le_acl_credits_to_reserve, 84 uint16_t br_edr_acl_credits_to_reserve) 85 : hci_transport_(hci_transport), 86 l2cap_channel_manager_(l2cap_channel_manager), 87 le_credits_(le_acl_credits_to_reserve), 88 br_edr_credits_(br_edr_acl_credits_to_reserve) {} 89 90 AclDataChannel(const AclDataChannel&) = delete; 91 AclDataChannel& operator=(const AclDataChannel&) = delete; 92 AclDataChannel(AclDataChannel&&) = delete; 93 AclDataChannel& operator=(AclDataChannel&&) = delete; 94 95 // Returns the max number of active ACL connections supported. GetMaxNumAclConnections()96 static constexpr size_t GetMaxNumAclConnections() { return kMaxConnections; } 97 98 // Revert to uninitialized state, clearing credit reservation and connections, 99 // but not the number of credits to reserve nor HCI transport. 100 void Reset(); 101 102 void ProcessReadBufferSizeCommandCompleteEvent( 103 emboss::ReadBufferSizeCommandCompleteEventWriter read_buffer_event); 104 105 // Acquires LE ACL credits for proxy host use by removing the amount needed 106 // from the amount that is passed to the host. ProcessLEReadBufferSizeCommandCompleteEvent(emboss::LEReadBufferSizeV1CommandCompleteEventWriter read_buffer_event)107 void ProcessLEReadBufferSizeCommandCompleteEvent( 108 emboss::LEReadBufferSizeV1CommandCompleteEventWriter read_buffer_event) { 109 ProcessSpecificLEReadBufferSizeCommandCompleteEvent(read_buffer_event); 110 } 111 ProcessLEReadBufferSizeCommandCompleteEvent(emboss::LEReadBufferSizeV2CommandCompleteEventWriter read_buffer_event)112 void ProcessLEReadBufferSizeCommandCompleteEvent( 113 emboss::LEReadBufferSizeV2CommandCompleteEventWriter read_buffer_event) { 114 ProcessSpecificLEReadBufferSizeCommandCompleteEvent(read_buffer_event); 115 } 116 117 // Remove completed packets from `nocp_event` as necessary to reclaim LE ACL 118 // credits that are associated with our credit-allocated connections. 119 void HandleNumberOfCompletedPacketsEvent(H4PacketWithHci&& h4_packet); 120 121 // Reclaim any credits we have associated with the removed connection and 122 // notify `L2capChannelManager` of disconnection. This function just processes 123 // the event; it does not handle forwarding it on. 124 void ProcessDisconnectionCompleteEvent(pw::span<uint8_t> hci_span); 125 126 // Create new tracked connection and pass on to host. 127 void HandleConnectionCompleteEvent(H4PacketWithHci&& h4_packet); 128 129 // Create new tracked connection and pass on to host. 130 void HandleLeConnectionCompleteEvent(H4PacketWithHci&& h4_packet); 131 132 // Create new tracked connection and pass on to host. 133 void HandleLeEnhancedConnectionCompleteV1Event(H4PacketWithHci&& h4_packet); 134 135 // Create new tracked connection and pass on to host. 136 void HandleLeEnhancedConnectionCompleteV2Event(H4PacketWithHci&& h4_packet); 137 138 /// Indicates whether the proxy has the capability of sending ACL packets. 139 /// Note that this indicates intention, so it can be true even if the proxy 140 /// has not yet or has been unable to reserve credits from the host. 141 bool HasSendAclCapability(AclTransportType transport) const; 142 143 /// @deprecated Use HasSendAclCapability with transport parameter instead. HasSendAclCapability()144 bool HasSendAclCapability() const { 145 return HasSendAclCapability(AclTransportType::kLe); 146 } 147 148 // Returns the number of available ACL send credits for the proxy. 149 // Can be zero if the controller has not yet been initialized by the host. 150 uint16_t GetNumFreeAclPackets(AclTransportType transport) const; 151 152 // In order to `SendAcl`, a `SendCredit` for the desired transport must be 153 // provided. 154 // 155 // Returns std::nullopt if no credits are available for the desired transport. 156 std::optional<SendCredit> ReserveSendCredit(AclTransportType transport); 157 158 // Send an ACL data packet contained in an H4 packet to the controller. 159 // Requires a reserved `SendCredit` that matches the transport of the 160 // connection on which `h4_packet` is to be sent. 161 // 162 // Returns PW_STATUS_UNAVAILABLE if no ACL send credits were available. 163 // Returns PW_STATUS_INVALID_ARGUMENT if ACL packet was ill-formed or `credit` 164 // was provided for the wrong transport. See logs. 165 // Returns PW_NOT_FOUND if ACL connection does not exist. 166 pw::Status SendAcl(H4PacketWithH4&& h4_packet, SendCredit&& credit); 167 168 // Register a new logical link on ACL logical transport. 169 // 170 // Returns PW_STATUS_OK if a connection was added. 171 // Returns PW_STATUS_ALREADY EXISTS if a connection already exists. 172 // Returns PW_STATUS_RESOURCE_EXHAUSTED if no space for additional connection. 173 pw::Status CreateAclConnection(uint16_t connection_handle, 174 AclTransportType transport); 175 176 // Returns the signaling channel for this link if `connection_handle` 177 // references a tracked connection and `local_cid` matches its id. 178 L2capSignalingChannel* FindSignalingChannel(uint16_t connection_handle, 179 uint16_t local_cid); 180 181 // Handles an ACL Data frame. 182 // Returns true if the frame was handled and is consumed by the proxy. 183 // Returns false if the frame should be passed on to the other side. 184 bool HandleAclData(AclDataChannel::Direction direction, 185 emboss::AclDataFrameWriter& acl); 186 187 private: 188 // An active logical link on ACL logical transport. 189 // TODO: https://pwbug.dev/360929142 - Encapsulate all logic related to this 190 // within a new LogicalLinkManager class? 191 class AclConnection { 192 public: 193 AclConnection(AclTransportType transport, 194 uint16_t connection_handle, 195 uint16_t num_pending_packets, 196 L2capChannelManager& l2cap_channel_manager); 197 198 AclConnection(const AclConnection&) = delete; 199 AclConnection& operator=(const AclConnection&) = delete; 200 AclConnection(AclConnection&&) = delete; 201 AclConnection& operator=(AclConnection&&) = default; 202 connection_handle()203 uint16_t connection_handle() const { return connection_handle_; } 204 num_pending_packets()205 uint16_t num_pending_packets() const { return num_pending_packets_; } 206 transport()207 AclTransportType transport() const { return transport_; } 208 set_num_pending_packets(uint16_t new_val)209 void set_num_pending_packets(uint16_t new_val) { 210 num_pending_packets_ = new_val; 211 } 212 signaling_channel()213 L2capSignalingChannel* signaling_channel() { 214 if (transport_ == AclTransportType::kLe) { 215 return &leu_signaling_channel_; 216 } else { 217 return &aclu_signaling_channel_; 218 } 219 } 220 221 // Returns true if recombination is active 222 // (currently receiving and recombining fragments). RecombinationActive(Direction direction)223 bool RecombinationActive(Direction direction) { 224 return bool(get_recombination_buffer(direction)); 225 } 226 227 // Starts a new recombination session. 228 // 229 // Precondition: Recombination must not already be active 230 // (RecombinationActive must be false). 231 // 232 // Returns: 233 // * FAILED_PRECONDITION if recombination is already active. 234 // * Any error from MultiBufWriter::Create(), namely RESOURCE_EXHAUSTED. 235 // * OK if recombination is started. 236 pw::Status StartRecombination( 237 Direction direction, 238 multibuf::MultiBufAllocator& multibuf_allocator, 239 size_t size); 240 241 // Adds a fragment of data to the recombination buffer. 242 // 243 // Precondition: Recombination must be active 244 // (RecombinationActive must be true). 245 // 246 // Returns: 247 // * FAILED_PRECONDITION if recombination is not active. 248 // * Any error from MultiBufWriter::Write(), namely RESOURCE_EXHAUSTED. 249 // * OK if the data was written, with value: 250 // * If recombination is incomplete, returns an empty MultiBuf. 251 // * If recombination is complete, returns a nonempty MultiBuf with the 252 // recombined data and ends recombination. 253 pw::Result<multibuf::MultiBuf> RecombineFragment( 254 Direction direction, pw::span<const uint8_t> data); 255 256 // Ends recombination. 257 void EndRecombination(Direction direction); 258 259 private: 260 AclTransportType transport_; 261 uint16_t connection_handle_; 262 uint16_t num_pending_packets_; 263 L2capLeUSignalingChannel leu_signaling_channel_; 264 // TODO: https://pwbug.dev/379172336 - Create correct signaling channel 265 // type based on link type. 266 L2capAclUSignalingChannel aclu_signaling_channel_; 267 268 std::array<std::optional<MultiBufWriter>, kNumDirections> 269 recombination_buffers_; 270 get_recombination_buffer(Direction direction)271 MultiBufWriter* get_recombination_buffer(Direction direction) { 272 auto& recomb = recombination_buffers_[cpp23::to_underlying(direction)]; 273 return recomb ? recomb.operator->() : nullptr; 274 } 275 }; 276 277 class Credits { 278 public: Credits(uint16_t to_reserve)279 explicit Credits(uint16_t to_reserve) : to_reserve_(to_reserve) {} 280 281 void Reset(); 282 283 // Reserves credits from the controllers max and returns how many the host 284 // can use. 285 uint16_t Reserve(uint16_t controller_max); 286 287 // Mark `num_credits` as pending. 288 // 289 // Returns Status::ResourceExhausted() if there aren't enough available 290 // credits. 291 Status MarkPending(uint16_t num_credits); 292 293 // Return `num_credits` to available pool. 294 void MarkCompleted(uint16_t num_credits); 295 Remaining()296 uint16_t Remaining() const { return proxy_max_ - proxy_pending_; } Available()297 bool Available() const { return Remaining() > 0; } 298 299 // If this class was initialized with some number of credits to reserve, 300 // return true. HasSendCapability()301 bool HasSendCapability() const { return to_reserve_ > 0; } 302 303 // If this class has already had credits reserved from the controller. Initialized()304 bool Initialized() const { return proxy_max_ > 0; } 305 306 private: 307 const uint16_t to_reserve_; 308 // The local number of HCI ACL Data packets that we have reserved for 309 // this proxy host to use. 310 uint16_t proxy_max_ = 0; 311 // The number of HCI ACL Data packets that we have sent to the controller 312 // and have not yet completed. 313 uint16_t proxy_pending_ = 0; 314 }; 315 316 // Returns pointer to AclConnection with provided `connection_handle` in 317 // `acl_connections_`. Returns nullptr if no such connection exists. 318 // 319 // Pointer is assured valid only as long as `mutex_` is held. 320 AclConnection* FindAclConnection(uint16_t connection_handle) 321 PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 322 323 Credits& LookupCredits(AclTransportType transport) 324 PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 325 326 const Credits& LookupCredits(AclTransportType transport) const 327 PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 328 329 void HandleLeConnectionCompleteEvent(uint16_t connection_handle, 330 emboss::StatusCode status); 331 332 // Data members 333 334 // Maximum number of active ACL connections supported. 335 // TODO: https://pwbug.dev/349700888 - Make size configurable. 336 static constexpr size_t kMaxConnections = 10; 337 338 // Reference to the transport owned by the host. 339 HciTransport& hci_transport_; 340 341 // TODO: https://pwbug.dev/360929142 - Remove this circular dependency. 342 L2capChannelManager& l2cap_channel_manager_; 343 344 // Credit allocation will happen inside a mutex since it crosses thread 345 // boundaries. The mutex also guards interactions with ACL connection objects. 346 mutable pw::sync::Mutex mutex_; 347 348 Credits le_credits_ PW_GUARDED_BY(mutex_); 349 Credits br_edr_credits_ PW_GUARDED_BY(mutex_); 350 351 // List of credit-allocated ACL connections. 352 pw::Vector<AclConnection, kMaxConnections> acl_connections_ 353 PW_GUARDED_BY(mutex_); 354 355 // Instantiated in acl_data_channel.cc for 356 // `emboss::LEReadBufferSizeV1CommandCompleteEventWriter` and 357 // `emboss::LEReadBufferSizeV1CommandCompleteEventWriter`. 358 template <class EventT> 359 void ProcessSpecificLEReadBufferSizeCommandCompleteEvent( 360 EventT read_buffer_event); 361 }; 362 363 } // namespace pw::bluetooth::proxy 364