xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/public/pw_bluetooth_proxy/l2cap_coc.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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