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_bluetooth_proxy/h4_packet.h"
18 #include "pw_bluetooth_proxy/internal/logical_transport.h"
19 #include "pw_containers/inline_queue.h"
20 #include "pw_containers/intrusive_forward_list.h"
21 #include "pw_result/result.h"
22 #include "pw_status/status.h"
23 #include "pw_sync/lock_annotations.h"
24 #include "pw_sync/mutex.h"
25 
26 namespace pw::bluetooth::proxy {
27 
28 class L2capChannelManager;
29 
30 // Base class for peer-to-peer L2CAP-based channels supporting writing.
31 //
32 // Derived channels should expose, at minimum, a "write" operation that
33 // takes a variable information payload. Protocol-dependent information that
34 // is fixed per channel, such as addresses, flags, handles, etc. should be
35 // provided at construction to derived channels.
36 class L2capWriteChannel : public IntrusiveForwardList<L2capWriteChannel>::Item {
37  public:
38   L2capWriteChannel(const L2capWriteChannel& other) = delete;
39   L2capWriteChannel& operator=(const L2capWriteChannel& other) = delete;
40   L2capWriteChannel(L2capWriteChannel&& other);
41   L2capWriteChannel& operator=(L2capWriteChannel&& other);
42 
43   virtual ~L2capWriteChannel();
44 
45   // Max number of Tx L2CAP packets that can be waiting to send.
QueueCapacity()46   static constexpr size_t QueueCapacity() { return kQueueCapacity; }
47 
48   // Queue L2CAP `packet` for sending and `ReportPacketsMayBeReadyToSend()`.
49   //
50   // Returns PW_STATUS_UNAVAILABLE if queue is full (transient error).
51   [[nodiscard]] virtual Status QueuePacket(H4PacketWithH4&& packet);
52 
53   // Dequeue a packet if one is available to send.
54   [[nodiscard]] virtual std::optional<H4PacketWithH4> DequeuePacket();
55 
56   // Get the destination L2CAP channel ID.
remote_cid()57   uint16_t remote_cid() const { return remote_cid_; }
58 
59   // Get the ACL connection handle.
connection_handle()60   uint16_t connection_handle() const { return connection_handle_; }
61 
transport()62   AclTransportType transport() const { return transport_; }
63 
64  protected:
65   explicit L2capWriteChannel(L2capChannelManager& l2cap_channel_manager,
66                              uint16_t connection_handle,
67                              AclTransportType transport,
68                              uint16_t remote_cid);
69 
70   // Returns whether or not ACL connection handle & destination L2CAP channel
71   // identifier are valid parameters for a packet.
72   [[nodiscard]] static bool AreValidParameters(uint16_t connection_handle,
73                                                uint16_t remote_cid);
74 
75   // Reserve an L2CAP over ACL over H4 packet, with those three headers
76   // populated for an L2CAP PDU payload of `data_length` bytes addressed to
77   // `connection_handle_`.
78   //
79   // Returns PW_STATUS_INVALID_ARGUMENT if payload is too large for a buffer.
80   // Returns PW_STATUS_UNAVAILABLE if all buffers are currently occupied.
81   pw::Result<H4PacketWithH4> PopulateTxL2capPacket(uint16_t data_length);
82 
83   // Returns the maximum size supported for Tx L2CAP PDU payloads.
84   uint16_t MaxL2capPayloadSize() const;
85 
86   // Alert `L2capChannelManager` that queued packets may be ready to send.
87   // When calling this method, ensure no locks are held that are also acquired
88   // in `Dequeue()` overrides.
89   void ReportPacketsMayBeReadyToSend();
90 
91   // Remove all packets from queue.
92   void ClearQueue();
93 
94  private:
95   static constexpr uint16_t kMaxValidConnectionHandle = 0x0EFF;
96 
97   // TODO: https://pwbug.dev/349700888 - Make capacity configurable.
98   static constexpr size_t kQueueCapacity = 5;
99 
100   AclTransportType transport_;
101 
102   // ACL connection handle on remote peer to which packets are sent.
103   uint16_t connection_handle_;
104 
105   // L2CAP channel ID on remote peer to which packets are sent.
106   uint16_t remote_cid_;
107 
108   // `L2capChannelManager` and channel may concurrently call functions that
109   // access queue.
110   //
111   // TODO: https://pwbug.dev/381942905 - This mutex is static to avoid it being
112   // destroyed when an L2capWriteChannel is erased from a container. When an
113   // L2capWriteChannel is erased, it is std::destroyed then overwritten by the
114   // subsequent L2capWriteChannel in the container via the move assignment
115   // operator. After this, the previously destroyed L2capWriteChannel object is
116   // again in an operational state, so its mutex needs to remain valid. This is
117   // a bug in pw::Vector::erase(). Once this behavior is fixed, we can remove
118   // the static to avoid cross-channel contention.
119   inline static sync::Mutex global_send_queue_mutex_;
120 
121   // Stores Tx L2CAP packets.
122   InlineQueue<H4PacketWithH4, kQueueCapacity> send_queue_
123       PW_GUARDED_BY(global_send_queue_mutex_);
124 
125   L2capChannelManager& l2cap_channel_manager_;
126 };
127 
128 }  // namespace pw::bluetooth::proxy
129