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