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