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/internal/l2cap_read_channel.h" 18 #include "pw_bluetooth_proxy/internal/l2cap_write_channel.h" 19 #include "pw_sync/mutex.h" 20 21 namespace pw::bluetooth::proxy { 22 23 /// L2CAP connection-oriented channel that supports writing to and reading 24 /// from a remote peer. 25 class L2capCoc : public L2capWriteChannel, public L2capReadChannel { 26 public: 27 /// Parameters for a direction of packet flow in an `L2capCoc`. 28 struct CocConfig { 29 /// Channel identifier of the endpoint. 30 /// For Rx: Local CID. 31 /// For Tx: Remote CID. 32 uint16_t cid; 33 /// Maximum Transmission Unit. 34 /// For Rx: Specified by local device. Indicates the maximum SDU size we are 35 /// capable of accepting. 36 /// For Tx: Specified by remote peer. Indicates the maximum SDU size we are 37 /// allowed to send. 38 uint16_t mtu; 39 /// Maximum PDU payload Size. 40 /// For Rx: Specified by local device. Indicates the maximum payload size 41 /// for an L2CAP packet we are capable of accepting. 42 /// For Tx: Specified by remote peer. Indicates the maximum payload size for 43 /// for an L2CAP packet we are allowed to send. 44 uint16_t mps; 45 /// For Rx: Tracks the number of credits we have currently apportioned to 46 /// the remote peer for sending us K-frames in LE Credit Based Flow 47 /// Control mode. 48 /// For Tx: Currently available credits for sending K-frames in LE Credit 49 /// Based Flow Control mode. This may be different from the initial 50 /// value if the container has already sent K-frames and/or received 51 /// credits. 52 uint16_t credits; 53 }; 54 55 enum class Event { 56 // TODO: https://pwbug.dev/360929142 - Listen for 57 // L2CAP_DISCONNECTION_REQ/RSP packets and report this event accordingly. 58 kChannelClosedByOther, 59 /// An invalid packet was received. The channel is now `kStopped` and should 60 /// be closed. See error logs for details. 61 kRxInvalid, 62 /// The channel has received a packet while in the `kStopped` state. The 63 /// channel should have been closed. 64 kRxWhileStopped, 65 /// PDU recombination is not yet supported, but a fragmented L2CAP frame has 66 /// been received. The channel is now `kStopped` and should be closed. 67 // TODO: https://pwbug.dev/365179076 - Support recombination. 68 kRxFragmented, 69 }; 70 71 L2capCoc(const L2capCoc& other) = delete; 72 L2capCoc& operator=(const L2capCoc& other) = delete; 73 /// Channel is moved on return from factory function, so client is responsible 74 /// for storing channel. 75 L2capCoc(L2capCoc&& other); 76 // TODO: https://pwbug.dev/360929142 - Define move assignment operator so 77 // `L2capCoc` can be erased from pw containers. 78 L2capCoc& operator=(L2capCoc&& other) = delete; 79 80 /// Enter `kStopped` state. This means 81 /// - Pending sends will not complete. 82 /// - Calls to `Write()` will return PW_STATUS_FAILED_PRECONDITION. 83 /// - Incoming packets will be dropped & trigger `kRxWhileStopped` events. 84 /// - Container is responsible for closing L2CAP connection & destructing 85 /// the channel object to free its resources. 86 /// 87 /// .. pw-status-codes:: 88 /// OK: If channel entered `kStopped` state. 89 /// INVALID_ARGUMENT: If channel was previously `kStopped`. 90 /// @endrst 91 pw::Status Stop(); 92 93 /// Send an L2CAP payload to the remote peer. 94 /// 95 /// @param[in] payload The L2CAP payload to be sent. Payload will be copied 96 /// before function completes. 97 /// 98 /// @returns @rst 99 /// 100 /// .. pw-status-codes:: 101 /// OK: If packet was successfully queued for send. 102 /// UNAVAILABLE: If channel could not acquire the resources to queue 103 /// the send at this time (transient error). 104 /// INVALID_ARGUMENT: If payload is too large. 105 /// FAILED_PRECONDITION: If channel is `kStopped`. 106 /// @endrst 107 pw::Status Write(pw::span<const uint8_t> payload); 108 109 protected: 110 static pw::Result<L2capCoc> Create( 111 L2capChannelManager& l2cap_channel_manager, 112 uint16_t connection_handle, 113 CocConfig rx_config, 114 CocConfig tx_config, 115 pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn, 116 pw::Function<void(Event event)>&& event_fn); 117 118 // `SendPayloadFromControllerToClient` with the information payload contained 119 // in `kframe`. As packet desegmentation is not supported, segmented SDUs are 120 // discarded. 121 bool HandlePduFromController(pw::span<uint8_t> kframe) override 122 PW_LOCKS_EXCLUDED(mutex_); 123 124 bool HandlePduFromHost(pw::span<uint8_t> kframe) override 125 PW_LOCKS_EXCLUDED(mutex_); 126 127 // Increment `send_credits_` by `credits`. 128 void AddCredits(uint16_t credits) PW_LOCKS_EXCLUDED(mutex_); 129 130 private: 131 enum class CocState { 132 kRunning, 133 kStopped, 134 }; 135 136 explicit L2capCoc(L2capChannelManager& l2cap_channel_manager, 137 uint16_t connection_handle, 138 CocConfig rx_config, 139 CocConfig tx_config, 140 pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn, 141 pw::Function<void(Event event)>&& event_fn); 142 143 // Stop channel & notify client. 144 void OnFragmentedPduReceived() override; 145 146 // `Stop()` channel if `kRunning` & call `event_fn_(error)` if it exists. 147 void StopChannelAndReportError(Event error); 148 149 // Override: Dequeue a packet only if a credit is able to be subtracted. 150 std::optional<H4PacketWithH4> DequeuePacket() override 151 PW_LOCKS_EXCLUDED(mutex_); 152 153 CocState state_; 154 sync::Mutex mutex_; 155 uint16_t rx_mtu_; 156 uint16_t rx_mps_; 157 uint16_t tx_mtu_; 158 uint16_t tx_mps_; 159 uint16_t tx_credits_ PW_GUARDED_BY(mutex_); 160 uint16_t remaining_sdu_bytes_to_ignore_ PW_GUARDED_BY(mutex_); 161 pw::Function<void(Event event)> event_fn_; 162 }; 163 164 } // namespace pw::bluetooth::proxy 165