• 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 "pw_assert/check.h"
18 #include "pw_bluetooth_proxy/h4_packet.h"
19 #include "pw_bluetooth_proxy/internal/logical_transport.h"
20 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
21 #include "pw_containers/inline_queue.h"
22 #include "pw_containers/intrusive_forward_list.h"
23 #include "pw_multibuf/allocator.h"
24 #include "pw_multibuf/multibuf.h"
25 #include "pw_result/result.h"
26 #include "pw_status/status.h"
27 #include "pw_sync/lock_annotations.h"
28 #include "pw_sync/mutex.h"
29 
30 namespace pw::bluetooth::proxy {
31 
32 class L2capChannelManager;
33 
34 // Base class for peer-to-peer L2CAP-based channels.
35 //
36 // Protocol-dependent information that is fixed per channel, such as addresses,
37 // flags, handles, etc. should be provided at construction to derived channels.
38 class L2capChannel : public IntrusiveForwardList<L2capChannel>::Item {
39  public:
40   enum class State {
41     kRunning,
42     // Channel is stopped, but the L2CAP connection has not been closed.
43     kStopped,
44     // L2CAP connection has been closed as the result of an
45     // HCI_Disconnection_Complete event, L2CAP_DISCONNECTION_RSP packet, or
46     // HCI_Reset Command packet; or `ProxyHost` dtor has been called.
47     kClosed,
48     // Channel has been moved from and is no longer a valid object.
49     kUndefined,
50   };
51 
52   L2capChannel(const L2capChannel& other) = delete;
53   L2capChannel& operator=(const L2capChannel& other) = delete;
54   // Channels are moved to the client after construction.
55   L2capChannel(L2capChannel&& other);
56   // Move assignment operator allows channels to be erased from pw::Vector.
57   L2capChannel& operator=(L2capChannel&& other);
58 
59   virtual ~L2capChannel();
60 
61   // Enter `State::kStopped`. This means
62   //   - Queue is cleared so pending sends will not complete.
63   //   - Calls to `QueuePacket()` will return PW_STATUS_FAILED_PRECONDITION, so
64   //     derived channels should not accept client writes.
65   //   - Rx packets will be dropped & trigger `kRxWhileStopped` events.
66   //   - Container is responsible for closing L2CAP connection & destructing
67   //     the channel object to free its resources.
68   void Stop();
69 
70   // Deregister the channel and enter `State::kClosed`. Closing a channel has
71   // the same effects as stopping the channel and triggers
72   // `L2capChannelEvent::kChannelClosedByOther`.
73   //
74   // Deregistered channels are not managed by the proxy, so any traffic
75   // addressed to/from them passes through `ProxyHost` unaffected. (Rx packets
76   // do not trigger `kRxWhileStopped` events.)
77   void Close();
78 
79   //-------------
80   //  Tx (public)
81   //-------------
82 
83   // TODO: https://pwbug.dev/388082771 - Most of these APIs should be in a
84   // public header. Will probably do that as part of moving them to
85   // ClientChannel.
86 
87   /// Send a payload to the remote peer.
88   ///
89   /// @param[in] payload The client payload to be sent. Payload will be
90   /// destroyed once its data has been used.
91   ///
92   /// @returns A StatusWithMultiBuf with one of the statuses below. If status is
93   /// not OK then payload is also returned in StatusWithMultiBuf.
94   ///
95   /// .. pw-status-codes::
96   ///  OK:                  If packet was successfully queued for send.
97   ///  UNAVAILABLE:         If channel could not acquire the resources to queue
98   ///                       the send at this time (transient error). If an
99   ///                       `event_fn` has been provided it will be called with
100   ///                       `L2capChannelEvent::kWriteAvailable` when there is
101   ///                       queue space available again.
102   ///  INVALID_ARGUMENT:    If payload is too large or if payload is not a
103   ///                       contiguous MultiBuf.
104   ///  FAILED_PRECONDITION: If channel is not `State::kRunning`.
105   ///  UNIMPLEMENTED:       If channel does not support Write(MultiBuf).
106   /// @endrst
107   // TODO: https://pwbug.dev/388082771 - Plan to eventually move this to
108   // ClientChannel.
109   virtual StatusWithMultiBuf Write(pw::multibuf::MultiBuf&& payload);
110 
111   /// Send an L2CAP payload to the remote peer.
112   ///
113   /// @param[in] payload The L2CAP payload to be sent. Payload will be copied
114   ///                    before function completes.
115   ///
116   /// @returns @rst
117   ///
118   /// .. pw-status-codes::
119   ///  OK:                  If packet was successfully queued for send.
120   ///  UNAVAILABLE:         If channel could not acquire the resources to queue
121   ///                       the send at this time (transient error). If an
122   ///                       `event_fn` has been provided it will be called with
123   ///                       `L2capChannelEvent::kWriteAvailable` when there is
124   ///                       queue space available again.
125   ///  INVALID_ARGUMENT:    If payload is too large.
126   ///  FAILED_PRECONDITION  If channel is not `State::kRunning`.
127   ///  UNIMPLEMENTED:       If channel does not support Write(MultiBuf).
128   /// @endrst
129   // Channels other than `L2capCoc` use this Write, but plan is to move them
130   // all to using Write(MultiBuf).
131   // TODO: https://pwbug.dev/379337272 - Delete this once all channels have
132   // transitioned to Write(MultiBuf).
133   virtual pw::Status Write(pw::span<const uint8_t> payload);
134 
135   /// Determine if channel is ready to accept one or more Write payloads.
136   ///
137   /// @returns @rst
138   ///
139   /// .. pw-status-codes::
140   ///    OK: Channel is ready to accept one or more Write payloads.
141   ///
142   ///    UNAVAILABLE: Channel does not yet have the resources to queue a Write
143   ///    at this time (transient error). If an `event_fn` has been provided it
144   ///    will be called with `L2capChannelEvent::kWriteAvailable` when there is
145   ///    queue space available again.
146   ///
147   ///    FAILED_PRECONDITION: If channel is not `State::kRunning`.
148   ///
149   /// @endrst
150   ///
151   Status IsWriteAvailable();
152 
153   // Dequeue a packet if one is available to send.
154   [[nodiscard]] virtual std::optional<H4PacketWithH4> DequeuePacket();
155 
156   // Max number of Tx L2CAP packets that can be waiting to send.
QueueCapacity()157   static constexpr size_t QueueCapacity() { return kQueueCapacity; }
158 
159   //-------------
160   //  Rx (public)
161   //-------------
162 
163   // Handle a Tx L2CAP PDU.
164   //
165   // Implementations should call `SendPayloadFromHostToClient` after processing
166   // the PDU.
167   //
168   // Return true if the PDU was consumed by the channel. Otherwise, return false
169   // and the PDU will be forwarded by `ProxyHost` on to the Bluetooth
170   // controller.
171   [[nodiscard]] virtual bool HandlePduFromHost(pw::span<uint8_t> l2cap_pdu) = 0;
172 
173   // Called when an L2CAP PDU is received on this channel. If channel is
174   // `kRunning`, returns `HandlePduFromController(l2cap_pdu)`. If channel is not
175   // `State::kRunning`, sends `kRxWhileStopped` event to client and drops PDU.
176   // This function will call DoHandlePduFromController on its subclass.
177   [[nodiscard]] bool HandlePduFromController(pw::span<uint8_t> l2cap_pdu);
178 
179   //--------------
180   //  Accessors:
181   //--------------
182 
state()183   State state() const { return state_; }
184 
local_cid()185   uint16_t local_cid() const { return local_cid_; }
186 
remote_cid()187   uint16_t remote_cid() const { return remote_cid_; }
188 
connection_handle()189   uint16_t connection_handle() const { return connection_handle_; }
190 
transport()191   AclTransportType transport() const { return transport_; }
192 
rx_multibuf_allocator()193   multibuf::MultiBufAllocator* rx_multibuf_allocator() const {
194     return rx_multibuf_allocator_;
195   }
196 
197  protected:
198   friend class L2capChannelManager;
199 
200   //----------------------
201   //  Creation (protected)
202   //----------------------
203 
204   explicit L2capChannel(
205       L2capChannelManager& l2cap_channel_manager,
206       multibuf::MultiBufAllocator* rx_multibuf_allocator,
207       uint16_t connection_handle,
208       AclTransportType transport,
209       uint16_t local_cid,
210       uint16_t remote_cid,
211       OptionalPayloadReceiveCallback&& payload_from_controller_fn,
212       OptionalPayloadReceiveCallback&& payload_from_host_fn,
213       ChannelEventCallback&& event_fn);
214 
215   // Returns whether or not ACL connection handle & L2CAP channel identifiers
216   // are valid parameters for a packet.
217   [[nodiscard]] static bool AreValidParameters(uint16_t connection_handle,
218                                                uint16_t local_cid,
219                                                uint16_t remote_cid);
220 
221   //-------------------
222   //  Other (protected)
223   //-------------------
224 
225   // Send `event` to client if an event callback was provided.
226   void SendEvent(L2capChannelEvent event);
227 
228   // Helper since these operations should typically be coupled.
StopAndSendEvent(L2capChannelEvent event)229   void StopAndSendEvent(L2capChannelEvent event) {
230     Stop();
231     SendEvent(event);
232   }
233 
234   // Called on channel closure, i.e. when the ACL connection or L2CAP connection
235   // is being dropped. Derived channels should override this to clean up state
236   // that is being invalidated, such as dangling references to the channel's
237   // underlying `AclConnection`.
238   virtual void DoClose() = 0;
239 
240   // Enter `State::kClosed` without deregistering. This has all the same effects
241   // as stopping the channel and triggers `event`. No-op if channel is already
242   // `State::kClosed`.
243   void InternalClose(
244       L2capChannelEvent event = L2capChannelEvent::kChannelClosedByOther);
245 
246   // For derived channels to use in lock annotations.
send_queue_mutex()247   const sync::Mutex& send_queue_mutex() const
248       PW_LOCK_RETURNED(send_queue_mutex_) {
249     return send_queue_mutex_;
250   }
251 
252   //----------------
253   //  Tx (protected)
254   //----------------
255 
256   // Channels that need to send a payload during handling a received packet
257   // directly (for instance to replenish credits) should use this function which
258   // does not take the L2capChannelManager channels lock.
WriteDuringRx(pw::multibuf::MultiBuf && payload)259   inline StatusWithMultiBuf WriteDuringRx(pw::multibuf::MultiBuf&& payload) {
260     return WriteLocked(std::move(payload));
261   }
262 
263   // Write payload to queue but don't drain the queue as this would require
264   // taking L2capChannelManager channel_mutex_ lock.
265   StatusWithMultiBuf WriteLocked(pw::multibuf::MultiBuf&& payload);
266 
267   // Queue L2CAP `packet` for sending and `ReportNewTxPacketsOrCredits()`.
268   //
269   // Returns PW_STATUS_UNAVAILABLE if queue is full (transient error).
270   // Returns PW_STATUS_FAILED_PRECONDITION if channel is not `State::kRunning`.
271   //
272   // Channels other than `L2capCoc` use QueuePacket(), but plan is to move them
273   // all to using QueuePayload().
274   // TODO: https://pwbug.dev/379337272 - Delete this once all channels have
275   // transitioned to QueuePayload.
276   [[nodiscard]] virtual Status QueuePacket(H4PacketWithH4&& packet);
277 
278   // Pop front buffer. Queue must be nonempty.
279   void PopFrontPayload() PW_EXCLUSIVE_LOCKS_REQUIRED(send_queue_mutex_);
280 
281   // Returns span over front buffer. Queue must be nonempty.
282   ConstByteSpan GetFrontPayloadSpan() const
283       PW_EXCLUSIVE_LOCKS_REQUIRED(send_queue_mutex_);
284 
285   bool PayloadQueueEmpty() const PW_EXCLUSIVE_LOCKS_REQUIRED(send_queue_mutex_);
286 
287   // Reserve an L2CAP over ACL over H4 packet, with those three headers
288   // populated for an L2CAP PDU payload of `data_length` bytes addressed to
289   // `connection_handle_`.
290   //
291   // Returns PW_STATUS_INVALID_ARGUMENT if payload is too large for a buffer.
292   // Returns PW_STATUS_UNAVAILABLE if all buffers are currently occupied.
293   pw::Result<H4PacketWithH4> PopulateTxL2capPacket(uint16_t data_length);
294 
295   // Return if we can generally handle the provided data length.
296   // Note PopulateTxL2capPacket can still fail if buffers or memory are not
297   // available at that time.
298   bool IsOkL2capDataLength(uint16_t data_length);
299 
300   // If all H4 buffers are occupied, this variant primes the kWriteAvailable
301   // event to be sent once buffer space becomes available again.
302   //
303   // TODO: https://pwbug.dev/379337272 - Once derived channels migrate to
304   // queueing client payloads on Write() instead of populating Tx packets, then
305   // delete this variant.
306   pw::Result<H4PacketWithH4> PopulateTxL2capPacketDuringWrite(
307       uint16_t data_length) PW_LOCKS_EXCLUDED(send_queue_mutex_);
308 
309   // Returns the maximum size supported for Tx L2CAP PDU payloads.
310   //
311   // Returns std::nullopt if LE_ACL_Data_Packet_Length was not yet provided in
312   // an LE_Read_Buffer_Size command complete event.
313   std::optional<uint16_t> MaxL2capPayloadSize() const;
314 
315   // Alert `L2capChannelManager` that queued packets may be ready to send.
316   void ReportNewTxPacketsOrCredits();
317 
318   // Tell `L2capChannelManager` to try and send all available queued
319   // packets. When calling this method, ensure no locks are held that are
320   // also acquired in `Dequeue()` overrides, and that the channels lock is
321   // not held either.
322   void DrainChannelQueuesIfNewTx() PW_LOCKS_EXCLUDED(send_queue_mutex_);
323 
324   // Remove all packets from queue.
325   void ClearQueue();
326 
327   //-------
328   //  Rx (protected)
329   //-------
330 
331   // Returns false if payload should be forwarded to controller instead.
332   virtual bool SendPayloadFromHostToClient(pw::span<uint8_t> payload);
333 
334   // Returns false if payload should be forwarded to host instead.
335   virtual bool SendPayloadFromControllerToClient(pw::span<uint8_t> payload);
336 
337  private:
338   static constexpr uint16_t kMaxValidConnectionHandle = 0x0EFF;
339 
340   // TODO: https://pwbug.dev/349700888 - Make capacity configurable.
341   static constexpr size_t kQueueCapacity = 5;
342 
343   // Return true if the current object uses payload_queue_.
344   // TODO: https://pwbug.dev/379337272 - Delete this once all channels have
345   // transitioned to payload_queue_.
346   virtual bool UsesPayloadQueue() = 0;
347 
348   // Returns false if payload should be forwarded to host instead.
349   bool SendPayloadToClient(pw::span<uint8_t> payload,
350                            OptionalPayloadReceiveCallback& callback);
351 
352   // Enter `State::kUndefined`, indicating that the channel has been moved from
353   // and is no longer a valid object.
354   void Undefine();
355 
356   // Helper for move constructor and move assignment.
357   void MoveFields(L2capChannel& other) PW_LOCKS_EXCLUDED(send_queue_mutex_);
358 
359   L2capChannelManager& l2cap_channel_manager_;
360 
361   State state_;
362 
363   // ACL connection handle.
364   uint16_t connection_handle_;
365 
366   AclTransportType transport_;
367 
368   // L2CAP channel ID of local endpoint.
369   uint16_t local_cid_;
370 
371   // L2CAP channel ID of remote endpoint.
372   uint16_t remote_cid_;
373 
374   // Notify clients of asynchronous events encountered such as errors.
375   ChannelEventCallback event_fn_;
376 
377   // Reserve an L2CAP packet over ACL over H4 packet.
378   pw::Result<H4PacketWithH4> PopulateL2capPacket(uint16_t data_length);
379 
380   //--------------
381   //  Tx (private)
382   //--------------
383 
384   // Queue a client `buf` for sending and `ReportNewTxPacketsOrCredits()`.
385   // Must be a contiguous MultiBuf.
386   //
387   // Returns PW_STATUS_UNAVAILABLE if queue is full (transient error).
388   // Returns PW_STATUS_FAILED_PRECONDITION if channel is not `State::kRunning`.
389   StatusWithMultiBuf QueuePayload(multibuf::MultiBuf&& buf)
390       PW_LOCKS_EXCLUDED(send_queue_mutex_);
391 
392   // Writes the contents of MultiBuf to the PDU queue (send_queue_).
393   //
394   // The contents of the MultiBuf are copied during the call and the MultiBuf
395   // is destroyed.
396   //
397   // Called for subclasses that don't do payload queueing (as determined by
398   // UsesPayloadQueue) during the transition.
399   // TODO: https://pwbug.dev/379337272 - Delete when all channels are
400   // transitioned to using payload queues.
401   StatusWithMultiBuf WriteToPduQueue(multibuf::MultiBuf&& payload);
402 
403   // Writes the MultiBuf to the payload queue (payload_queue_).
404   //
405   // Called for subclasses that don't do payload queueing (as determined by
406   // UsesPayloadQueue) during the transition.
407   // TODO: https://pwbug.dev/379337272 - Delete when all channels are
408   // transitioned to using payload queues.
409   StatusWithMultiBuf WriteToPayloadQueue(multibuf::MultiBuf&& payload);
410 
411   // Return the next Tx PDU based on the client's queued payloads. If the
412   // returned PDU will complete the transmission of a payload, that payload
413   // should be popped from the queue. If no payloads are queued, return
414   // std::nullopt.
415   //
416   // Note this is overrode by `L2capCoc` which uses `payload_queue_` rather than
417   // `send_queue_`. The plan is to move all channels to using `payload_queue_`.
418   // TODO: https://pwbug.dev/379337272 - Make pure virtual once all derived
419   // channels implement this method.
420   virtual std::optional<H4PacketWithH4> GenerateNextTxPacket()
421       PW_EXCLUSIVE_LOCKS_REQUIRED(send_queue_mutex_);
422 
423   // `L2capChannelManager` and channel may concurrently call functions that
424   // access queue.
425   sync::Mutex send_queue_mutex_;
426 
427   // Stores Tx L2CAP packets.
428   //
429   // This queue is used for channels other than `L2capCoc`, but we plan to
430   // transition all channels to using `payload_queue_` below.
431   // TODO: https://pwbug.dev/379337272 - Delete this once all channels have
432   // transitioned to payload_queue_.
433   InlineQueue<H4PacketWithH4, kQueueCapacity> send_queue_
434       PW_GUARDED_BY(send_queue_mutex_);
435 
436   // Stores client Tx payload buffers.
437   InlineQueue<multibuf::MultiBuf, kQueueCapacity> payload_queue_
438       PW_GUARDED_BY(send_queue_mutex_);
439 
440   // True if the last queue attempt didn't have space. Will be cleared on
441   // successful dequeue.
442   bool notify_on_dequeue_ PW_GUARDED_BY(send_queue_mutex_) = false;
443 
444   //--------------
445   //  Rx (private)
446   //--------------
447 
448   // Handle an Rx L2CAP PDU.
449   //
450   // Implementations should call `SendPayloadFromControllerToClient` after
451   // recombining/processing the PDU (e.g. after updating channel state and
452   // screening out certain PDUs).
453   //
454   // Return true if the PDU was consumed by the channel. Otherwise, return false
455   // and the PDU will be forwarded by `ProxyHost` on to the Bluetooth host.
456   [[nodiscard]] virtual bool DoHandlePduFromController(
457       pw::span<uint8_t> l2cap_pdu) = 0;
458 
459   //--------------
460   //  Data members
461   //--------------
462 
463   // Optional client-provided multibuf allocator.
464   multibuf::MultiBufAllocator* rx_multibuf_allocator_;
465 
466   // Client-provided controller read callback.
467   OptionalPayloadReceiveCallback payload_from_controller_fn_;
468   // Client-provided host read callback.
469   OptionalPayloadReceiveCallback payload_from_host_fn_;
470 };
471 
472 }  // namespace pw::bluetooth::proxy
473