1 // Copyright 2023 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 #include <memory> 17 #include <unordered_map> 18 19 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 20 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 21 #include "pw_bluetooth_sapphire/internal/host/common/packet_view.h" 22 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h" 23 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" 24 #include "pw_bluetooth_sapphire/internal/host/l2cap/scoped_channel.h" 25 26 namespace bt::l2cap { 27 28 class Channel; 29 30 namespace internal { 31 32 using SignalingPacket = PacketView<CommandHeader>; 33 using MutableSignalingPacket = MutablePacketView<CommandHeader>; 34 35 using DataCallback = fit::function<void(const ByteBuffer& data)>; 36 using SignalingPacketHandler = 37 fit::function<void(const SignalingPacket& packet)>; 38 39 // SignalingChannelInterface contains the procedures that command flows use to 40 // send and receive signaling channel transactions. 41 class SignalingChannelInterface { 42 public: 43 // Action in response to a request-type packet. 44 enum class Status { 45 kSuccess, // Remote response received 46 kReject, // Remote rejection received 47 kTimeOut, // Timed out waiting for matching remote command 48 }; 49 50 // ResponseHandler return value. Indicates whether additional responses are 51 // expected in this transaction (e.g. in the case of receiving a response with 52 // a pending status or continuation flag). 53 enum class ResponseHandlerAction { 54 kExpectAdditionalResponse, 55 // No additional responses expected in this transaction. 56 kCompleteOutboundTransaction, 57 }; 58 59 // Callback invoked to handle a response received from the remote. If |status| 60 // is kSuccess or kReject, then |rsp_payload| will contain any payload 61 // received. This callback is allowed to destroy the SignalingChannel, but 62 // must return kCompleteOutboundTransaction if it does. 63 using ResponseHandler = fit::function<ResponseHandlerAction( 64 Status, const ByteBuffer& rsp_payload)>; 65 66 // Initiate an outbound transaction. The signaling channel will send a request 67 // then expect reception of one or more responses with a code one greater than 68 // the request. Each response or rejection received invokes |cb|. When |cb| 69 // returns false, it will be removed. Returns false if the request failed to 70 // send. 71 virtual bool SendRequest(CommandCode req_code, 72 const ByteBuffer& payload, 73 ResponseHandler cb) = 0; 74 75 // Send a command packet in response to an incoming request. 76 class Responder { 77 public: 78 // Send a response that corresponds to the request received 79 virtual void Send(const ByteBuffer& rsp_payload) = 0; 80 81 // Reject invalid, malformed, or unhandled request 82 virtual void RejectNotUnderstood() = 0; 83 84 // Reject request non-existent or otherwise invalid channel ID(s) 85 virtual void RejectInvalidChannelId(ChannelId local_cid, 86 ChannelId remote_cid) = 0; 87 88 protected: 89 virtual ~Responder() = default; 90 }; 91 92 // Callback invoked to handle a request received from the remote. 93 // |req_payload| contains any payload received, without the command header. 94 // The callee can use |responder| to respond or reject. Parameters passed to 95 // this handler are only guaranteed to be valid while the handler is running. 96 using RequestDelegate = 97 fit::function<void(const ByteBuffer& req_payload, Responder* responder)>; 98 99 // Register a handler for all inbound transactions matching |req_code|, which 100 // should be the code of a request. |cb| will be called with request payloads 101 // received, and is expected to respond to, reject, or ignore the requests. 102 // Calls to this function with a previously registered |req_code| will replace 103 // the current delegate. 104 virtual void ServeRequest(CommandCode req_code, RequestDelegate cb) = 0; 105 106 protected: 107 virtual ~SignalingChannelInterface() = default; 108 }; 109 110 // SignalingChannel is an abstract class that handles the common operations 111 // involved in LE and BR/EDR signaling channels. 112 // 113 // TODO(armansito): Implement flow control (RTX/ERTX timers). 114 class SignalingChannel : public SignalingChannelInterface { 115 public: 116 SignalingChannel(Channel::WeakPtr chan, 117 pw::bluetooth::emboss::ConnectionRole role, 118 pw::async::Dispatcher& dispatcher); 119 ~SignalingChannel() override = default; 120 121 // SignalingChannelInterface overrides 122 bool SendRequest(CommandCode req_code, 123 const ByteBuffer& payload, 124 ResponseHandler cb) override; 125 void ServeRequest(CommandCode req_code, RequestDelegate cb) override; 126 is_open()127 bool is_open() const { return is_open_; } 128 129 // Local signaling MTU (i.e. MTU_sig, per spec) mtu()130 uint16_t mtu() const { return mtu_; } set_mtu(uint16_t mtu)131 void set_mtu(uint16_t mtu) { mtu_ = mtu; } 132 133 protected: 134 // Implementation for responding to a request that binds the request's 135 // identifier and the response's code so that the client's |Send| invocation 136 // does not need to supply them nor even know them. 137 class ResponderImpl : public Responder { 138 public: 139 ResponderImpl(SignalingChannel* sig, CommandCode code, CommandId id); 140 void Send(const ByteBuffer& rsp_payload) override; 141 void RejectNotUnderstood() override; 142 void RejectInvalidChannelId(ChannelId local_cid, 143 ChannelId remote_cid) override; 144 145 private: sig()146 SignalingChannel* sig() const { return sig_; } 147 148 SignalingChannel* const sig_; 149 const CommandCode code_; 150 const CommandId id_; 151 }; 152 153 // Sends out a single signaling packet using the given parameters. 154 bool SendPacket(CommandCode code, uint8_t identifier, const ByteBuffer& data); 155 156 // True if the code is for a supported response-type signaling command. 157 virtual bool IsSupportedResponse(CommandCode code) const = 0; 158 159 // Called when a frame is received to decode into L2CAP signaling command 160 // packets. The derived implementation should invoke |cb| for each packet with 161 // a valid payload length, send a Command Reject packet for each packet with 162 // an intact ID in its header but invalid payload length, and drop any other 163 // incoming data. 164 virtual void DecodeRxUnit(ByteBufferPtr sdu, 165 const SignalingPacketHandler& cb) = 0; 166 167 // Called when a new signaling packet has been received. Returns false if 168 // |packet| is rejected. Otherwise returns true and sends a response packet. 169 // 170 // This method is thread-safe in that a SignalingChannel cannot be deleted 171 // while this is running. SendPacket() can be called safely from this method. 172 // TODO(fxbug.dev/42056068): make non-virtual & private after removing le 173 // signaling channel override 174 virtual bool HandlePacket(const SignalingPacket& packet); 175 176 // Sends out a command reject packet with the given parameters. 177 bool SendCommandReject(uint8_t identifier, 178 RejectReason reason, 179 const ByteBuffer& data); 180 181 // Returns true if called on this SignalingChannel's creation thread. Mainly 182 // intended for debug assertions. 183 184 // Returns the logical link that signaling channel is operating on. role()185 pw::bluetooth::emboss::ConnectionRole role() const { return role_; } 186 187 // Generates a command identifier in sequential order that is never 188 // kInvalidId. The caller is responsible for bookkeeping when reusing command 189 // IDs to prevent collisions with pending commands. 190 CommandId GetNextCommandId(); 191 192 private: 193 // Enqueue a response to a request with command id |id| and payload 194 // |request_packet|. Register a callback |cb| that will be invoked when a 195 // response-type command packet (specified by |response_command_code|) is 196 // received. Starts the RTX timer and handles retransmission of 197 // |request_packet| and eventual timeout failure if a response isn't received. 198 // If the signaling channel receives a Command Reject that matches the same 199 // |id|, the rejection packet will be forwarded to the callback instead. 200 void EnqueueResponse(const ByteBuffer& request_packet, 201 CommandId id, 202 CommandCode response_command_code, 203 ResponseHandler cb); 204 205 // Called when a response-type command packet is received. Sends a Command 206 // Reject if no ResponseHandler was registered for inbound packet's command 207 // code and identifier. 208 void OnRxResponse(const SignalingPacket& packet); 209 210 // Called after Response Timeout eXpired (RTX) or Extended Response Timeout 211 // eXpired (ERTX) timer expires. |id| must be in |pending_commands_|. If 212 // |retransmit| is true, requests will be retransmitted up to the 213 // retransmission limit before timing out the response. The ResponseHandler 214 // will be invoked with Status::kTimeOut and an empty ByteBuffer. 215 void OnResponseTimeout(CommandId id, bool retransmit); 216 217 // True if an outbound request-type command has registered a callback for its 218 // response matching a particular |id|. 219 bool IsCommandPending(CommandId id) const; 220 221 // Sends out the given signaling packet directly via |chan_| after running 222 // debug-mode assertions for validity. Packet must correspond to exactly one 223 // C-frame payload. 224 // 225 // This method is not thread-safe (i.e. requires external locking). 226 // 227 // TODO(armansito): This should be generalized for ACL-U to allow multiple 228 // signaling commands in a single C-frame. 229 bool Send(ByteBufferPtr packet); 230 231 // Builds a signaling packet with the given parameters and payload. The 232 // backing buffer is slab allocated. 233 ByteBufferPtr BuildPacket(CommandCode code, 234 uint8_t identifier, 235 const ByteBuffer& data); 236 // Channel callbacks: 237 void OnChannelClosed(); 238 void OnRxBFrame(ByteBufferPtr sdu); 239 240 // Invoke the abstract packet handler |HandlePacket| for well-formed command 241 // packets and send responses for command packets that exceed this host's MTU 242 // or can't be handled by this host. 243 void CheckAndDispatchPacket(const SignalingPacket& packet); 244 245 // Stores copy of request, response handlers, and timeout state for requests 246 // that have been sent. 247 struct PendingCommand { PendingCommandPendingCommand248 PendingCommand(const ByteBuffer& request_packet, 249 CommandCode response_command_code, 250 ResponseHandler response_handler_cb, 251 pw::async::Dispatcher& dispatcher) 252 : response_code(response_command_code), 253 response_handler(std::move(response_handler_cb)), 254 command_packet(std::make_unique<DynamicByteBuffer>(request_packet)), 255 transmit_count(1u), 256 timer_duration(0u), 257 response_timeout_task(dispatcher) {} 258 CommandCode response_code; 259 ResponseHandler response_handler; 260 261 // Copy of request command packet. Used for retransmissions. 262 ByteBufferPtr command_packet; 263 264 // Number of times this request has been transmitted. 265 size_t transmit_count; 266 267 // The current timer duration. Used to perform exponential backoff with the 268 // RTX timer. 269 pw::chrono::SystemClock::duration timer_duration; 270 271 // Automatically canceled by destruction if the response is received. 272 SmartTask response_timeout_task; 273 }; 274 275 // Retransmit the request corresponding to |pending_command| and reset the RTX 276 // timer. 277 void RetransmitPendingCommand(PendingCommand& pending_command); 278 279 pw::async::Dispatcher& pw_dispatcher_; 280 281 bool is_open_; 282 l2cap::ScopedChannel chan_; 283 pw::bluetooth::emboss::ConnectionRole role_; 284 uint16_t mtu_; 285 uint8_t next_cmd_id_; 286 287 // Stores response handlers for outbound request packets with the 288 // corresponding CommandId. 289 std::unordered_map<CommandId, PendingCommand> pending_commands_; 290 291 // Stores handlers for incoming request packets. 292 std::unordered_map<CommandCode, RequestDelegate> inbound_handlers_; 293 294 WeakSelf<SignalingChannel> weak_self_; 295 296 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SignalingChannel); 297 }; 298 299 } // namespace internal 300 } // namespace bt::l2cap 301