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 <atomic> 18 #include <mutex> 19 #include <optional> 20 21 #include "pw_bluetooth_proxy/internal/acl_data_channel.h" 22 #include "pw_bluetooth_proxy/internal/h4_storage.h" 23 #include "pw_bluetooth_proxy/internal/l2cap_channel.h" 24 #include "pw_bluetooth_proxy/internal/l2cap_status_tracker.h" 25 #include "pw_bluetooth_proxy/l2cap_channel_common.h" 26 27 namespace pw::bluetooth::proxy { 28 29 // `L2capChannelManager` mediates between `ProxyHost` and the L2CAP-based 30 // channels held by clients of `ProxyHost`, such as L2CAP connection-oriented 31 // channels, GATT Notify channels, and RFCOMM channels. 32 // 33 // ACL packet transmission is subject to data control flow, managed by 34 // `AclDataChannel`. `L2capChannelManager` handles queueing Tx packets when 35 // credits are unavailable and sending Tx packets as credits become available, 36 // dequeueing packets in FIFO order per channel and in round robin fashion 37 // around channels. 38 class L2capChannelManager { 39 public: 40 // Wrapper for locked access to L2capChannel. Lock must be held at 41 // construction already, and will be released on destruct. 42 class LockedL2capChannel { 43 public: LockedL2capChannel(L2capChannel & channel,std::unique_lock<sync::Mutex> && lock)44 LockedL2capChannel(L2capChannel& channel, 45 std::unique_lock<sync::Mutex>&& lock) 46 : channel_(&channel), lock_(std::move(lock)) {} 47 LockedL2capChannel(LockedL2capChannel && other)48 LockedL2capChannel(LockedL2capChannel&& other) 49 : channel_(other.channel_), lock_(std::move(other.lock_)) { 50 other.channel_ = nullptr; 51 } 52 53 LockedL2capChannel& operator=(LockedL2capChannel&& other) { 54 lock_ = std::move(other.lock_); 55 channel_ = other.channel_; 56 other.channel_ = nullptr; 57 return *this; 58 } 59 LockedL2capChannel(const LockedL2capChannel&) = delete; 60 LockedL2capChannel& operator=(const LockedL2capChannel&) = delete; 61 62 // Will assert if accessed on moved-from object. channel()63 L2capChannel& channel() { 64 PW_ASSERT(channel_); 65 return *channel_; 66 } 67 68 private: 69 L2capChannel* channel_; 70 std::unique_lock<sync::Mutex> lock_; 71 }; 72 73 L2capChannelManager(AclDataChannel& acl_data_channel); 74 75 // Start proxying L2CAP packets addressed to `channel` arriving from 76 // the controller and allow `channel` to send & queue Tx L2CAP packets. 77 void RegisterChannel(L2capChannel& channel) 78 PW_LOCKS_EXCLUDED(channels_mutex_); 79 80 // Stop proxying L2CAP packets addressed to `channel` and stop sending L2CAP 81 // packets queued in `channel`, if `channel` is currently registered. 82 void DeregisterChannel(L2capChannel& channel) 83 PW_LOCKS_EXCLUDED(channels_mutex_); 84 85 // Deregister and close all channels then propagate `event` to clients. 86 void DeregisterAndCloseChannels(L2capChannelEvent event) 87 PW_LOCKS_EXCLUDED(channels_mutex_); 88 89 // Get an `H4PacketWithH4` backed by a buffer in `H4Storage` able to hold 90 // `size` bytes of data. 91 // 92 // Returns PW_STATUS_UNAVAILABLE if all buffers are currently occupied. 93 // Returns PW_STATUS_INVALID_ARGUMENT if `size` is too large for a buffer. 94 pw::Result<H4PacketWithH4> GetAclH4Packet(uint16_t size); 95 96 // Report that new tx packets have been queued or new tx credits have been 97 // received since the last DrainChannelQueuesIfNewTx. 98 void ReportNewTxPacketsOrCredits(); 99 100 // Send L2CAP packets queued in registered channels. Since this function takes 101 // the channels_mutex_ lock, it can't be directly called while handling a 102 // received packet on a channel. Instead call ReportPacketsMayBeReadyToSend(). 103 // Rx processing will then call this function when complete. 104 void DrainChannelQueuesIfNewTx() PW_LOCKS_EXCLUDED(channels_mutex_); 105 106 // Drain channel queues even if no channel explicitly requested it. Should be 107 // used for events triggering queue space at the ACL level. 108 void ForceDrainChannelQueues() PW_LOCKS_EXCLUDED(channels_mutex_); 109 110 // Returns the size of an H4 buffer reserved for Tx packets. 111 uint16_t GetH4BuffSize() const; 112 113 std::optional<LockedL2capChannel> FindChannelByLocalCid( 114 uint16_t connection_handle, uint16_t local_cid); 115 116 std::optional<LockedL2capChannel> FindChannelByRemoteCid( 117 uint16_t connection_handle, uint16_t remote_cid); 118 119 // Must be called with channels_mutex_ held. 120 L2capChannel* FindChannelByLocalCidLocked(uint16_t connection_handle, 121 uint16_t local_cid); 122 123 // Must be called with channels_mutex_ held. 124 L2capChannel* FindChannelByRemoteCidLocked(uint16_t connection_handle, 125 uint16_t remote_cid); 126 127 // Register for notifications of connection and disconnection for a 128 // particular L2cap service identified by its PSM. 129 void RegisterStatusDelegate(L2capStatusDelegate& delegate); 130 131 // Unregister a service delegate. 132 void UnregisterStatusDelegate(L2capStatusDelegate& delegate); 133 134 // Called when a l2cap channel connection successfully made. 135 void HandleConnectionComplete(const L2capChannelConnectionInfo& info); 136 137 // Called when an ACL connection is disconnected. 138 void HandleAclDisconnectionComplete(uint16_t connection_handle); 139 140 // Called when a l2cap channel connection is disconnected. 141 // 142 // Must be called under channels_mutex_ but we can't use proper lock 143 // annotation here since the call comes via signaling channel. 144 // TODO: https://pwbug.dev/390511432 - Figure out way to add annotations to 145 // enforce this invariant. 146 void HandleDisconnectionCompleteLocked( 147 const L2capStatusTracker::DisconnectParams& params); 148 149 // Deliver any pending connection events. Should not be called while holding 150 // channels_mutex_. 151 void DeliverPendingEvents(); 152 153 // Core Spec v6.0 Vol 4, Part E, Section 7.8.2: "The LE_ACL_Data_Packet_Length 154 // parameter shall be used to determine the maximum size of the L2CAP PDU 155 // fragments that are contained in ACL data packets". A value of 0 means "No 156 // dedicated LE Buffer exists". 157 // 158 // Return std::nullopt if HCI_LE_Read_Buffer_Size command complete event has 159 // not yet been received. 160 // 161 // TODO: https://pwbug.dev/379339642 - Add tests to confirm this value caps 162 // the size of Tx L2capCoc segments when segmentation is implemented. le_acl_data_packet_length()163 std::optional<uint16_t> le_acl_data_packet_length() const { 164 return le_acl_data_packet_length_; 165 } 166 set_le_acl_data_packet_length(uint16_t le_acl_data_packet_length)167 void set_le_acl_data_packet_length(uint16_t le_acl_data_packet_length) { 168 le_acl_data_packet_length_ = le_acl_data_packet_length; 169 } 170 171 private: 172 // Circularly advance `it`, wrapping around to front if `it` reaches the end. 173 void Advance(IntrusiveForwardList<L2capChannel>::iterator& it) 174 PW_EXCLUSIVE_LOCKS_REQUIRED(channels_mutex_); 175 176 // Stop proxying L2CAP packets addressed to `channel` and stop sending L2CAP 177 // packets queued in `channel`, if `channel` is currently registered. 178 void DeregisterChannelLocked(L2capChannel& channel) 179 PW_EXCLUSIVE_LOCKS_REQUIRED(channels_mutex_); 180 181 // Reference to the ACL data channel owned by the proxy. 182 AclDataChannel& acl_data_channel_; 183 184 // Owns H4 packet buffers. 185 H4Storage h4_storage_; 186 187 std::atomic<std::optional<uint16_t>> le_acl_data_packet_length_{std::nullopt}; 188 189 // Enforce mutual exclusion of all operations on channels. 190 sync::Mutex channels_mutex_; 191 192 // List of registered L2CAP channels. 193 IntrusiveForwardList<L2capChannel> channels_ PW_GUARDED_BY(channels_mutex_); 194 195 // Iterator to "least recently drained" channel. 196 IntrusiveForwardList<L2capChannel>::iterator lrd_channel_ 197 PW_GUARDED_BY(channels_mutex_); 198 199 // Iterator to final channel to be visited in ongoing round robin. 200 IntrusiveForwardList<L2capChannel>::iterator round_robin_terminus_ 201 PW_GUARDED_BY(channels_mutex_); 202 203 // True if new tx packets have been queued or new tx credits have been 204 // received since the last DrainChannelQueuesIfNewTx. 205 std::atomic_bool new_tx_since_drain_ = false; 206 207 // Channel connection status tracker and delegate holder. 208 L2capStatusTracker status_tracker_; 209 }; 210 211 } // namespace pw::bluetooth::proxy 212