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