• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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