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