• 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 <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