xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/l2cap_coc.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_bluetooth_proxy/l2cap_coc.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <mutex>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_bluetooth/emboss_util.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_bluetooth/hci_data.emb.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_bluetooth/l2cap_frames.emb.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_bluetooth_proxy/internal/l2cap_write_channel.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
25*61c4878aSAndroid Build Coastguard Worker 
26*61c4878aSAndroid Build Coastguard Worker namespace pw::bluetooth::proxy {
27*61c4878aSAndroid Build Coastguard Worker 
L2capCoc(L2capCoc && other)28*61c4878aSAndroid Build Coastguard Worker L2capCoc::L2capCoc(L2capCoc&& other)
29*61c4878aSAndroid Build Coastguard Worker     : L2capWriteChannel(std::move(static_cast<L2capWriteChannel&>(other))),
30*61c4878aSAndroid Build Coastguard Worker       L2capReadChannel(std::move(static_cast<L2capReadChannel&>(other))),
31*61c4878aSAndroid Build Coastguard Worker       state_(other.state_),
32*61c4878aSAndroid Build Coastguard Worker       rx_mtu_(other.rx_mtu_),
33*61c4878aSAndroid Build Coastguard Worker       rx_mps_(other.rx_mps_),
34*61c4878aSAndroid Build Coastguard Worker       tx_mtu_(other.tx_mtu_),
35*61c4878aSAndroid Build Coastguard Worker       tx_mps_(other.tx_mps_),
36*61c4878aSAndroid Build Coastguard Worker       tx_credits_(other.tx_credits_),
37*61c4878aSAndroid Build Coastguard Worker       remaining_sdu_bytes_to_ignore_(other.remaining_sdu_bytes_to_ignore_),
38*61c4878aSAndroid Build Coastguard Worker       event_fn_(std::move(other.event_fn_)) {}
39*61c4878aSAndroid Build Coastguard Worker 
Stop()40*61c4878aSAndroid Build Coastguard Worker pw::Status L2capCoc::Stop() {
41*61c4878aSAndroid Build Coastguard Worker   if (state_ == CocState::kStopped) {
42*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
43*61c4878aSAndroid Build Coastguard Worker   }
44*61c4878aSAndroid Build Coastguard Worker   state_ = CocState::kStopped;
45*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
46*61c4878aSAndroid Build Coastguard Worker }
47*61c4878aSAndroid Build Coastguard Worker 
Write(pw::span<const uint8_t> payload)48*61c4878aSAndroid Build Coastguard Worker pw::Status L2capCoc::Write(pw::span<const uint8_t> payload) {
49*61c4878aSAndroid Build Coastguard Worker   if (state_ == CocState::kStopped) {
50*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
51*61c4878aSAndroid Build Coastguard Worker   }
52*61c4878aSAndroid Build Coastguard Worker 
53*61c4878aSAndroid Build Coastguard Worker   if (payload.size() > tx_mtu_) {
54*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
55*61c4878aSAndroid Build Coastguard Worker         "Payload (%zu bytes) exceeds MTU (%d bytes). So will not process.",
56*61c4878aSAndroid Build Coastguard Worker         payload.size(),
57*61c4878aSAndroid Build Coastguard Worker         tx_mtu_);
58*61c4878aSAndroid Build Coastguard Worker     return pw::Status::InvalidArgument();
59*61c4878aSAndroid Build Coastguard Worker   }
60*61c4878aSAndroid Build Coastguard Worker   // We do not currently support segmentation, so the payload is required to fit
61*61c4878aSAndroid Build Coastguard Worker   // within the remote peer's Maximum PDU payload Size.
62*61c4878aSAndroid Build Coastguard Worker   // TODO: https://pwbug.dev/360932103 - Support packet segmentation.
63*61c4878aSAndroid Build Coastguard Worker   if (payload.size() > tx_mps_) {
64*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
65*61c4878aSAndroid Build Coastguard Worker         "Payload (%zu bytes) exceeds MPS (%d bytes). So will not process.",
66*61c4878aSAndroid Build Coastguard Worker         payload.size(),
67*61c4878aSAndroid Build Coastguard Worker         tx_mps_);
68*61c4878aSAndroid Build Coastguard Worker     return pw::Status::InvalidArgument();
69*61c4878aSAndroid Build Coastguard Worker   }
70*61c4878aSAndroid Build Coastguard Worker 
71*61c4878aSAndroid Build Coastguard Worker   // 2 bytes for SDU length field
72*61c4878aSAndroid Build Coastguard Worker   size_t l2cap_data_length = payload.size() + 2;
73*61c4878aSAndroid Build Coastguard Worker   pw::Result<H4PacketWithH4> h4_result =
74*61c4878aSAndroid Build Coastguard Worker       PopulateTxL2capPacket(l2cap_data_length);
75*61c4878aSAndroid Build Coastguard Worker   if (!h4_result.ok()) {
76*61c4878aSAndroid Build Coastguard Worker     // This can fail as a result of the L2CAP PDU not fitting in an H4 buffer
77*61c4878aSAndroid Build Coastguard Worker     // or if all buffers are occupied.
78*61c4878aSAndroid Build Coastguard Worker     // TODO: https://pwbug.dev/365179076 - Once we support ACL fragmentation,
79*61c4878aSAndroid Build Coastguard Worker     // this function will not fail due to the L2CAP PDU size not fitting.
80*61c4878aSAndroid Build Coastguard Worker     return h4_result.status();
81*61c4878aSAndroid Build Coastguard Worker   }
82*61c4878aSAndroid Build Coastguard Worker   H4PacketWithH4 h4_packet = std::move(*h4_result);
83*61c4878aSAndroid Build Coastguard Worker 
84*61c4878aSAndroid Build Coastguard Worker   PW_TRY_ASSIGN(
85*61c4878aSAndroid Build Coastguard Worker       auto acl,
86*61c4878aSAndroid Build Coastguard Worker       MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan()));
87*61c4878aSAndroid Build Coastguard Worker   // Write payload.
88*61c4878aSAndroid Build Coastguard Worker   PW_TRY_ASSIGN(auto kframe,
89*61c4878aSAndroid Build Coastguard Worker                 MakeEmbossWriter<emboss::FirstKFrameWriter>(
90*61c4878aSAndroid Build Coastguard Worker                     acl.payload().BackingStorage().data(),
91*61c4878aSAndroid Build Coastguard Worker                     acl.payload().BackingStorage().SizeInBytes()));
92*61c4878aSAndroid Build Coastguard Worker   kframe.sdu_length().Write(payload.size());
93*61c4878aSAndroid Build Coastguard Worker   std::memcpy(
94*61c4878aSAndroid Build Coastguard Worker       kframe.payload().BackingStorage().data(), payload.data(), payload.size());
95*61c4878aSAndroid Build Coastguard Worker 
96*61c4878aSAndroid Build Coastguard Worker   return QueuePacket(std::move(h4_packet));
97*61c4878aSAndroid Build Coastguard Worker }
98*61c4878aSAndroid Build Coastguard Worker 
Create(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,CocConfig rx_config,CocConfig tx_config,pw::Function<void (pw::span<uint8_t> payload)> && receive_fn,pw::Function<void (Event event)> && event_fn)99*61c4878aSAndroid Build Coastguard Worker pw::Result<L2capCoc> L2capCoc::Create(
100*61c4878aSAndroid Build Coastguard Worker     L2capChannelManager& l2cap_channel_manager,
101*61c4878aSAndroid Build Coastguard Worker     uint16_t connection_handle,
102*61c4878aSAndroid Build Coastguard Worker     CocConfig rx_config,
103*61c4878aSAndroid Build Coastguard Worker     CocConfig tx_config,
104*61c4878aSAndroid Build Coastguard Worker     pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn,
105*61c4878aSAndroid Build Coastguard Worker     pw::Function<void(Event event)>&& event_fn) {
106*61c4878aSAndroid Build Coastguard Worker   if (!L2capReadChannel::AreValidParameters(connection_handle, rx_config.cid) ||
107*61c4878aSAndroid Build Coastguard Worker       !L2capWriteChannel::AreValidParameters(connection_handle,
108*61c4878aSAndroid Build Coastguard Worker                                              tx_config.cid)) {
109*61c4878aSAndroid Build Coastguard Worker     return pw::Status::InvalidArgument();
110*61c4878aSAndroid Build Coastguard Worker   }
111*61c4878aSAndroid Build Coastguard Worker 
112*61c4878aSAndroid Build Coastguard Worker   if (tx_config.mps < emboss::L2capLeCreditBasedConnectionReq::min_mps() ||
113*61c4878aSAndroid Build Coastguard Worker       tx_config.mps > emboss::L2capLeCreditBasedConnectionReq::max_mps()) {
114*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
115*61c4878aSAndroid Build Coastguard Worker         "Tx MPS (%d octets) invalid. L2CAP implementations shall support a "
116*61c4878aSAndroid Build Coastguard Worker         "minimum MPS of 23 octets and may support an MPS up to 65533 octets.",
117*61c4878aSAndroid Build Coastguard Worker         tx_config.mps);
118*61c4878aSAndroid Build Coastguard Worker     return pw::Status::InvalidArgument();
119*61c4878aSAndroid Build Coastguard Worker   }
120*61c4878aSAndroid Build Coastguard Worker 
121*61c4878aSAndroid Build Coastguard Worker   return L2capCoc(/*l2cap_channel_manager=*/l2cap_channel_manager,
122*61c4878aSAndroid Build Coastguard Worker                   /*connection_handle=*/connection_handle,
123*61c4878aSAndroid Build Coastguard Worker                   /*rx_config=*/rx_config,
124*61c4878aSAndroid Build Coastguard Worker                   /*tx_config=*/tx_config,
125*61c4878aSAndroid Build Coastguard Worker                   /*receive_fn=*/std::move(receive_fn),
126*61c4878aSAndroid Build Coastguard Worker                   /*event_fn=*/std::move(event_fn));
127*61c4878aSAndroid Build Coastguard Worker }
128*61c4878aSAndroid Build Coastguard Worker 
HandlePduFromController(pw::span<uint8_t> kframe)129*61c4878aSAndroid Build Coastguard Worker bool L2capCoc::HandlePduFromController(pw::span<uint8_t> kframe) {
130*61c4878aSAndroid Build Coastguard Worker   // TODO: https://pwbug.dev/360934030 - Track rx_credits.
131*61c4878aSAndroid Build Coastguard Worker   if (state_ == CocState::kStopped) {
132*61c4878aSAndroid Build Coastguard Worker     StopChannelAndReportError(Event::kRxWhileStopped);
133*61c4878aSAndroid Build Coastguard Worker     return true;
134*61c4878aSAndroid Build Coastguard Worker   }
135*61c4878aSAndroid Build Coastguard Worker 
136*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(mutex_);
137*61c4878aSAndroid Build Coastguard Worker   // If `remaining_sdu_bytes_to_ignore_` is nonzero, we are in state where we
138*61c4878aSAndroid Build Coastguard Worker   // are dropping continuing PDUs in a segmented SDU.
139*61c4878aSAndroid Build Coastguard Worker   if (remaining_sdu_bytes_to_ignore_ > 0) {
140*61c4878aSAndroid Build Coastguard Worker     Result<emboss::SubsequentKFrameView> subsequent_kframe_view =
141*61c4878aSAndroid Build Coastguard Worker         MakeEmbossView<emboss::SubsequentKFrameView>(kframe);
142*61c4878aSAndroid Build Coastguard Worker     if (!subsequent_kframe_view.ok()) {
143*61c4878aSAndroid Build Coastguard Worker       PW_LOG_ERROR(
144*61c4878aSAndroid Build Coastguard Worker           "(CID 0x%X) Buffer is too small for subsequent L2CAP K-frame. So "
145*61c4878aSAndroid Build Coastguard Worker           "will drop.",
146*61c4878aSAndroid Build Coastguard Worker           local_cid());
147*61c4878aSAndroid Build Coastguard Worker       return true;
148*61c4878aSAndroid Build Coastguard Worker     }
149*61c4878aSAndroid Build Coastguard Worker     PW_LOG_INFO(
150*61c4878aSAndroid Build Coastguard Worker         "(CID 0x%X) Dropping PDU that is part of current segmented SDU.",
151*61c4878aSAndroid Build Coastguard Worker         local_cid());
152*61c4878aSAndroid Build Coastguard Worker     if (subsequent_kframe_view->payload_size().Read() >
153*61c4878aSAndroid Build Coastguard Worker         remaining_sdu_bytes_to_ignore_) {
154*61c4878aSAndroid Build Coastguard Worker       // Core Spec v6.0 Vol 3, Part A, 3.4.3: "If the sum of the payload sizes
155*61c4878aSAndroid Build Coastguard Worker       // for the K-frames exceeds the specified SDU length, the receiver shall
156*61c4878aSAndroid Build Coastguard Worker       // disconnect the channel."
157*61c4878aSAndroid Build Coastguard Worker       PW_LOG_ERROR(
158*61c4878aSAndroid Build Coastguard Worker           "(CID 0x%X) Sum of K-frame payload sizes exceeds the specified SDU "
159*61c4878aSAndroid Build Coastguard Worker           "length. So stopping channel & reporting it needs to be closed.",
160*61c4878aSAndroid Build Coastguard Worker           local_cid());
161*61c4878aSAndroid Build Coastguard Worker       StopChannelAndReportError(Event::kRxInvalid);
162*61c4878aSAndroid Build Coastguard Worker     } else {
163*61c4878aSAndroid Build Coastguard Worker       remaining_sdu_bytes_to_ignore_ -=
164*61c4878aSAndroid Build Coastguard Worker           subsequent_kframe_view->payload_size().Read();
165*61c4878aSAndroid Build Coastguard Worker     }
166*61c4878aSAndroid Build Coastguard Worker     return true;
167*61c4878aSAndroid Build Coastguard Worker   }
168*61c4878aSAndroid Build Coastguard Worker 
169*61c4878aSAndroid Build Coastguard Worker   Result<emboss::FirstKFrameView> kframe_view =
170*61c4878aSAndroid Build Coastguard Worker       MakeEmbossView<emboss::FirstKFrameView>(kframe);
171*61c4878aSAndroid Build Coastguard Worker   if (!kframe_view.ok()) {
172*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
173*61c4878aSAndroid Build Coastguard Worker         "(CID 0x%X) Buffer is too small for L2CAP K-frame. So stopping channel "
174*61c4878aSAndroid Build Coastguard Worker         "& reporting it needs to be closed.",
175*61c4878aSAndroid Build Coastguard Worker         local_cid());
176*61c4878aSAndroid Build Coastguard Worker     StopChannelAndReportError(Event::kRxInvalid);
177*61c4878aSAndroid Build Coastguard Worker     return true;
178*61c4878aSAndroid Build Coastguard Worker   }
179*61c4878aSAndroid Build Coastguard Worker   uint16_t sdu_length = kframe_view->sdu_length().Read();
180*61c4878aSAndroid Build Coastguard Worker   uint16_t payload_size = kframe_view->payload_size().Read();
181*61c4878aSAndroid Build Coastguard Worker 
182*61c4878aSAndroid Build Coastguard Worker   // Core Spec v6.0 Vol 3, Part A, 3.4.3: "If the SDU length field value exceeds
183*61c4878aSAndroid Build Coastguard Worker   // the receiver's MTU, the receiver shall disconnect the channel."
184*61c4878aSAndroid Build Coastguard Worker   if (sdu_length > rx_mtu_) {
185*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
186*61c4878aSAndroid Build Coastguard Worker         "(CID 0x%X) Rx K-frame SDU exceeds MTU. So stopping channel & "
187*61c4878aSAndroid Build Coastguard Worker         "reporting it needs to be closed.",
188*61c4878aSAndroid Build Coastguard Worker         local_cid());
189*61c4878aSAndroid Build Coastguard Worker     StopChannelAndReportError(Event::kRxInvalid);
190*61c4878aSAndroid Build Coastguard Worker     return true;
191*61c4878aSAndroid Build Coastguard Worker   }
192*61c4878aSAndroid Build Coastguard Worker 
193*61c4878aSAndroid Build Coastguard Worker   // TODO: https://pwbug.dev/360932103 - Support SDU de-segmentation.
194*61c4878aSAndroid Build Coastguard Worker   // We don't support SDU de-segmentation yet. If we see a SDU size larger than
195*61c4878aSAndroid Build Coastguard Worker   // the current PDU size, we ignore that first PDU and all remaining PDUs for
196*61c4878aSAndroid Build Coastguard Worker   // that SDU (which we track via remaining bytes expected for the SDU).
197*61c4878aSAndroid Build Coastguard Worker   if (sdu_length > payload_size) {
198*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
199*61c4878aSAndroid Build Coastguard Worker         "(CID 0x%X) Encountered segmented L2CAP SDU (which is not yet "
200*61c4878aSAndroid Build Coastguard Worker         "supported). So will drop all PDUs in SDU.",
201*61c4878aSAndroid Build Coastguard Worker         local_cid());
202*61c4878aSAndroid Build Coastguard Worker     remaining_sdu_bytes_to_ignore_ = sdu_length - payload_size;
203*61c4878aSAndroid Build Coastguard Worker     return true;
204*61c4878aSAndroid Build Coastguard Worker   }
205*61c4878aSAndroid Build Coastguard Worker 
206*61c4878aSAndroid Build Coastguard Worker   // Core Spec v6.0 Vol 3, Part A, 3.4.3: "If the payload size of any K-frame
207*61c4878aSAndroid Build Coastguard Worker   // exceeds the receiver's MPS, the receiver shall disconnect the channel."
208*61c4878aSAndroid Build Coastguard Worker   if (payload_size > rx_mps_) {
209*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
210*61c4878aSAndroid Build Coastguard Worker         "(CID 0x%X) Rx K-frame payload exceeds MPU. So stopping channel & "
211*61c4878aSAndroid Build Coastguard Worker         "reporting it needs to be closed.",
212*61c4878aSAndroid Build Coastguard Worker         local_cid());
213*61c4878aSAndroid Build Coastguard Worker     StopChannelAndReportError(Event::kRxInvalid);
214*61c4878aSAndroid Build Coastguard Worker     return true;
215*61c4878aSAndroid Build Coastguard Worker   }
216*61c4878aSAndroid Build Coastguard Worker 
217*61c4878aSAndroid Build Coastguard Worker   SendPayloadFromControllerToClient(pw::span(
218*61c4878aSAndroid Build Coastguard Worker       const_cast<uint8_t*>(kframe_view->payload().BackingStorage().data()),
219*61c4878aSAndroid Build Coastguard Worker       kframe_view->payload_size().Read()));
220*61c4878aSAndroid Build Coastguard Worker   return true;
221*61c4878aSAndroid Build Coastguard Worker }
222*61c4878aSAndroid Build Coastguard Worker 
HandlePduFromHost(pw::span<uint8_t>)223*61c4878aSAndroid Build Coastguard Worker bool L2capCoc::HandlePduFromHost(pw::span<uint8_t>) PW_LOCKS_EXCLUDED(mutex_) {
224*61c4878aSAndroid Build Coastguard Worker   // Always forward data from host to controller
225*61c4878aSAndroid Build Coastguard Worker   return false;
226*61c4878aSAndroid Build Coastguard Worker }
227*61c4878aSAndroid Build Coastguard Worker 
L2capCoc(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,CocConfig rx_config,CocConfig tx_config,pw::Function<void (pw::span<uint8_t> payload)> && receive_fn,pw::Function<void (Event event)> && event_fn)228*61c4878aSAndroid Build Coastguard Worker L2capCoc::L2capCoc(L2capChannelManager& l2cap_channel_manager,
229*61c4878aSAndroid Build Coastguard Worker                    uint16_t connection_handle,
230*61c4878aSAndroid Build Coastguard Worker                    CocConfig rx_config,
231*61c4878aSAndroid Build Coastguard Worker                    CocConfig tx_config,
232*61c4878aSAndroid Build Coastguard Worker                    pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn,
233*61c4878aSAndroid Build Coastguard Worker                    pw::Function<void(Event event)>&& event_fn)
234*61c4878aSAndroid Build Coastguard Worker     : L2capWriteChannel(l2cap_channel_manager,
235*61c4878aSAndroid Build Coastguard Worker                         connection_handle,
236*61c4878aSAndroid Build Coastguard Worker                         AclTransportType::kLe,
237*61c4878aSAndroid Build Coastguard Worker                         tx_config.cid),
238*61c4878aSAndroid Build Coastguard Worker       L2capReadChannel(l2cap_channel_manager,
239*61c4878aSAndroid Build Coastguard Worker                        std::move(receive_fn),
240*61c4878aSAndroid Build Coastguard Worker                        connection_handle,
241*61c4878aSAndroid Build Coastguard Worker                        rx_config.cid),
242*61c4878aSAndroid Build Coastguard Worker       state_(CocState::kRunning),
243*61c4878aSAndroid Build Coastguard Worker       rx_mtu_(rx_config.mtu),
244*61c4878aSAndroid Build Coastguard Worker       rx_mps_(rx_config.mps),
245*61c4878aSAndroid Build Coastguard Worker       tx_mtu_(tx_config.mtu),
246*61c4878aSAndroid Build Coastguard Worker       tx_mps_(tx_config.mps),
247*61c4878aSAndroid Build Coastguard Worker       tx_credits_(tx_config.credits),
248*61c4878aSAndroid Build Coastguard Worker       remaining_sdu_bytes_to_ignore_(0),
249*61c4878aSAndroid Build Coastguard Worker       event_fn_(std::move(event_fn)) {}
250*61c4878aSAndroid Build Coastguard Worker 
OnFragmentedPduReceived()251*61c4878aSAndroid Build Coastguard Worker void L2capCoc::OnFragmentedPduReceived() {
252*61c4878aSAndroid Build Coastguard Worker   L2capReadChannel::OnFragmentedPduReceived();
253*61c4878aSAndroid Build Coastguard Worker   StopChannelAndReportError(Event::kRxFragmented);
254*61c4878aSAndroid Build Coastguard Worker }
255*61c4878aSAndroid Build Coastguard Worker 
StopChannelAndReportError(Event error)256*61c4878aSAndroid Build Coastguard Worker void L2capCoc::StopChannelAndReportError(Event error) {
257*61c4878aSAndroid Build Coastguard Worker   Stop().IgnoreError();
258*61c4878aSAndroid Build Coastguard Worker   if (event_fn_) {
259*61c4878aSAndroid Build Coastguard Worker     event_fn_(error);
260*61c4878aSAndroid Build Coastguard Worker   }
261*61c4878aSAndroid Build Coastguard Worker }
262*61c4878aSAndroid Build Coastguard Worker 
DequeuePacket()263*61c4878aSAndroid Build Coastguard Worker std::optional<H4PacketWithH4> L2capCoc::DequeuePacket() {
264*61c4878aSAndroid Build Coastguard Worker   if (state_ == CocState::kStopped) {
265*61c4878aSAndroid Build Coastguard Worker     return std::nullopt;
266*61c4878aSAndroid Build Coastguard Worker   }
267*61c4878aSAndroid Build Coastguard Worker 
268*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(mutex_);
269*61c4878aSAndroid Build Coastguard Worker   if (tx_credits_ == 0) {
270*61c4878aSAndroid Build Coastguard Worker     return std::nullopt;
271*61c4878aSAndroid Build Coastguard Worker   }
272*61c4878aSAndroid Build Coastguard Worker 
273*61c4878aSAndroid Build Coastguard Worker   std::optional<H4PacketWithH4> maybe_packet =
274*61c4878aSAndroid Build Coastguard Worker       L2capWriteChannel::DequeuePacket();
275*61c4878aSAndroid Build Coastguard Worker   if (maybe_packet.has_value()) {
276*61c4878aSAndroid Build Coastguard Worker     --tx_credits_;
277*61c4878aSAndroid Build Coastguard Worker   }
278*61c4878aSAndroid Build Coastguard Worker   return maybe_packet;
279*61c4878aSAndroid Build Coastguard Worker }
280*61c4878aSAndroid Build Coastguard Worker 
AddCredits(uint16_t credits)281*61c4878aSAndroid Build Coastguard Worker void L2capCoc::AddCredits(uint16_t credits) {
282*61c4878aSAndroid Build Coastguard Worker   if (state_ == CocState::kStopped) {
283*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
284*61c4878aSAndroid Build Coastguard Worker         "(CID 0x%X) Received credits on stopped CoC. So will ignore signal.",
285*61c4878aSAndroid Build Coastguard Worker         local_cid());
286*61c4878aSAndroid Build Coastguard Worker     return;
287*61c4878aSAndroid Build Coastguard Worker   }
288*61c4878aSAndroid Build Coastguard Worker 
289*61c4878aSAndroid Build Coastguard Worker   bool credits_previously_zero;
290*61c4878aSAndroid Build Coastguard Worker   {
291*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(mutex_);
292*61c4878aSAndroid Build Coastguard Worker 
293*61c4878aSAndroid Build Coastguard Worker     // Core Spec v6.0 Vol 3, Part A, 10.1: "The device receiving the credit
294*61c4878aSAndroid Build Coastguard Worker     // packet shall disconnect the L2CAP channel if the credit count exceeds
295*61c4878aSAndroid Build Coastguard Worker     // 65535."
296*61c4878aSAndroid Build Coastguard Worker     if (credits > emboss::L2capLeCreditBasedConnectionReq::max_credit_value() -
297*61c4878aSAndroid Build Coastguard Worker                       tx_credits_) {
298*61c4878aSAndroid Build Coastguard Worker       StopChannelAndReportError(Event::kRxInvalid);
299*61c4878aSAndroid Build Coastguard Worker       return;
300*61c4878aSAndroid Build Coastguard Worker     }
301*61c4878aSAndroid Build Coastguard Worker 
302*61c4878aSAndroid Build Coastguard Worker     credits_previously_zero = tx_credits_ == 0;
303*61c4878aSAndroid Build Coastguard Worker     tx_credits_ += credits;
304*61c4878aSAndroid Build Coastguard Worker   }
305*61c4878aSAndroid Build Coastguard Worker   if (credits_previously_zero) {
306*61c4878aSAndroid Build Coastguard Worker     ReportPacketsMayBeReadyToSend();
307*61c4878aSAndroid Build Coastguard Worker   }
308*61c4878aSAndroid Build Coastguard Worker }
309*61c4878aSAndroid Build Coastguard Worker 
310*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::bluetooth::proxy
311