• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <cstdint>
16 
17 #include "pw_assert/check.h"
18 #include "pw_bluetooth_proxy/h4_packet.h"
19 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
20 #include "pw_bluetooth_proxy_private/test_utils.h"
21 #include "pw_containers/flat_map.h"
22 
23 namespace pw::bluetooth::proxy {
24 namespace {
25 
26 using containers::FlatMap;
27 
28 // ########## L2capCocTest
29 
30 class L2capCocTest : public ProxyHostTest {};
31 
TEST_F(L2capCocTest,CannotCreateChannelWithInvalidArgs)32 TEST_F(L2capCocTest, CannotCreateChannelWithInvalidArgs) {
33   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
34       [](H4PacketWithHci&&) {});
35   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
36       [](H4PacketWithH4&&) {});
37 
38   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
39                               std::move(send_to_controller_fn),
40                               /*le_acl_credits_to_reserve=*/0,
41                               /*br_edr_acl_credits_to_reserve=*/0);
42 
43   // Connection handle too large by 1.
44   EXPECT_EQ(BuildCocWithResult(proxy, CocParameters{.handle = 0x0FFF}).status(),
45             PW_STATUS_INVALID_ARGUMENT);
46 
47   // Local CID invalid (0).
48   EXPECT_EQ(BuildCocWithResult(proxy, CocParameters{.local_cid = 0}).status(),
49             PW_STATUS_INVALID_ARGUMENT);
50 
51   // Remote CID invalid (0).
52   EXPECT_EQ(BuildCocWithResult(proxy, CocParameters{.remote_cid = 0}).status(),
53             PW_STATUS_INVALID_ARGUMENT);
54 }
55 
56 // ########## L2capCocWriteTest
57 
58 class L2capCocWriteTest : public ProxyHostTest {};
59 
60 // Size of sdu_length field in first K-frames.
61 constexpr uint8_t kSduLengthFieldSize = 2;
62 // Size of a K-Frame over Acl packet with no payload.
63 constexpr uint8_t kFirstKFrameOverAclMinSize =
64     emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
65     emboss::FirstKFrame::MinSizeInBytes();
66 
TEST_F(L2capCocWriteTest,BasicWrite)67 TEST_F(L2capCocWriteTest, BasicWrite) {
68   struct {
69     int sends_called = 0;
70     // First four bits 0x0 encode PB & BC flags
71     uint16_t handle = 0x0ACB;
72     // Length of L2CAP PDU
73     uint16_t acl_data_total_length = 0x0009;
74     // L2CAP header PDU length field
75     uint16_t pdu_length = 0x0005;
76     // Random CID
77     uint16_t channel_id = 0x1234;
78     // Length of L2CAP SDU
79     uint16_t sdu_length = 0x0003;
80     // L2CAP information payload
81     std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
82 
83     // Built from the preceding values in little endian order (except payload in
84     // big endian).
85     std::array<uint8_t, 13> expected_hci_packet = {0xCB,
86                                                    0x0A,
87                                                    0x09,
88                                                    0x00,
89                                                    0x05,
90                                                    0x00,
91                                                    0x34,
92                                                    0x12,
93                                                    0x03,
94                                                    0x00,
95                                                    0xAB,
96                                                    0xCD,
97                                                    0xEF};
98   } capture;
99 
100   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
101       []([[maybe_unused]] H4PacketWithHci&& packet) {});
102   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
103       [&capture](H4PacketWithH4&& packet) {
104         ++capture.sends_called;
105         EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
106         EXPECT_EQ(packet.GetHciSpan().size(),
107                   capture.expected_hci_packet.size());
108         EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
109                                packet.GetHciSpan().end(),
110                                capture.expected_hci_packet.begin(),
111                                capture.expected_hci_packet.end()));
112         PW_TEST_ASSERT_OK_AND_ASSIGN(
113             auto acl,
114             MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
115         EXPECT_EQ(acl.header().handle().Read(), capture.handle);
116         EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
117                   emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
118         EXPECT_EQ(acl.header().broadcast_flag().Read(),
119                   emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
120         EXPECT_EQ(acl.data_total_length().Read(),
121                   capture.acl_data_total_length);
122         emboss::FirstKFrameView kframe = emboss::MakeFirstKFrameView(
123             acl.payload().BackingStorage().data(), acl.SizeInBytes());
124         EXPECT_EQ(kframe.pdu_length().Read(), capture.pdu_length);
125         EXPECT_EQ(kframe.channel_id().Read(), capture.channel_id);
126         EXPECT_EQ(kframe.sdu_length().Read(), capture.sdu_length);
127         for (size_t i = 0; i < 3; ++i) {
128           EXPECT_EQ(kframe.payload()[i].Read(), capture.payload[i]);
129         }
130       });
131 
132   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
133                               std::move(send_to_controller_fn),
134                               /*le_acl_credits_to_reserve=*/1,
135                               /*br_edr_acl_credits_to_reserve=*/0);
136   // Allow proxy to reserve 1 credit.
137   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
138 
139   L2capCoc channel = BuildCoc(proxy,
140                               CocParameters{.handle = capture.handle,
141                                             .remote_cid = capture.channel_id});
142   PW_TEST_EXPECT_OK(
143       channel.Write(MultiBufFromSpan(span(capture.payload))).status);
144   EXPECT_EQ(capture.sends_called, 1);
145 }
146 
TEST_F(L2capCocWriteTest,ErrorOnWriteToStoppedChannel)147 TEST_F(L2capCocWriteTest, ErrorOnWriteToStoppedChannel) {
148   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
149       []([[maybe_unused]] H4PacketWithHci&& packet) {});
150   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
151       []([[maybe_unused]] H4PacketWithH4&& packet) {});
152   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
153                               std::move(send_to_controller_fn),
154                               /*le_acl_credits_to_reserve=*/1,
155                               /*br_edr_acl_credits_to_reserve=*/0);
156   // Allow proxy to reserve 1 credit.
157   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
158 
159   L2capCoc channel = BuildCoc(
160       proxy,
161       CocParameters{.handle = 123,
162                     .tx_credits = 1,
163                     .event_fn = []([[maybe_unused]] L2capChannelEvent event) {
164                       FAIL();
165                     }});
166 
167   channel.Stop();
168   EXPECT_EQ(channel.IsWriteAvailable(), PW_STATUS_FAILED_PRECONDITION);
169   EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status,
170             Status::FailedPrecondition());
171 }
172 
TEST_F(L2capCocWriteTest,WriteExceedingMtuFails)173 TEST_F(L2capCocWriteTest, WriteExceedingMtuFails) {
174   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
175       []([[maybe_unused]] H4PacketWithHci&& packet) {});
176   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
177       []([[maybe_unused]] H4PacketWithH4&& packet) { FAIL(); });
178 
179   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
180                               std::move(send_to_controller_fn),
181                               /*le_acl_credits_to_reserve=*/1,
182                               /*br_edr_acl_credits_to_reserve=*/0);
183   // Allow proxy to reserve 1 credit.
184   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
185 
186   // Payload size exceeds MTU.
187   L2capCoc small_mtu_channel = BuildCoc(proxy, CocParameters{.tx_mtu = 1});
188   std::array<uint8_t, 24> payload;
189   EXPECT_EQ(small_mtu_channel.Write(MultiBufFromSpan(span(payload))).status,
190             Status::InvalidArgument());
191 }
192 
TEST_F(L2capCocWriteTest,MultipleWritesSameChannel)193 TEST_F(L2capCocWriteTest, MultipleWritesSameChannel) {
194   struct {
195     int sends_called = 0;
196     std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
197   } capture;
198 
199   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
200       []([[maybe_unused]] H4PacketWithHci&& packet) {});
201   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
202       [&capture](H4PacketWithH4&& packet) {
203         ++capture.sends_called;
204         PW_TEST_ASSERT_OK_AND_ASSIGN(
205             auto acl,
206             MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
207         emboss::FirstKFrameView kframe = emboss::MakeFirstKFrameView(
208             acl.payload().BackingStorage().data(), acl.SizeInBytes());
209         for (size_t i = 0; i < 3; ++i) {
210           EXPECT_EQ(kframe.payload()[i].Read(), capture.payload[i]);
211         }
212       });
213 
214   uint16_t num_writes = 5;
215   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
216                               std::move(send_to_controller_fn),
217                               /*le_acl_credits_to_reserve=*/num_writes,
218                               /*br_edr_acl_credits_to_reserve=*/0);
219   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
220       proxy,
221       /*num_credits_to_reserve=*/num_writes));
222 
223   L2capCoc channel = BuildCoc(proxy, CocParameters{.tx_credits = num_writes});
224   for (int i = 0; i < num_writes; ++i) {
225     PW_TEST_EXPECT_OK(
226         channel.Write(MultiBufFromSpan(span(capture.payload))).status);
227     std::for_each(capture.payload.begin(),
228                   capture.payload.end(),
229                   [](uint8_t& byte) { ++byte; });
230   }
231 
232   EXPECT_EQ(capture.sends_called, num_writes);
233 }
234 
235 // Verify we get unavailable when send queue is full due to running out
236 // of  ACL credits. And that it reports available again once send queue is no
237 // longer full.
238 // TODO: https://pwbug.dev/380299794 - Add equivalent test for other channel
239 // types.
TEST_F(L2capCocWriteTest,FlowControlDueToAclCredits)240 TEST_F(L2capCocWriteTest, FlowControlDueToAclCredits) {
241   uint16_t kHandle = 123;
242   // Should align with L2capChannel::kQueueCapacity.
243   static constexpr size_t kL2capQueueCapacity = 5;
244   // We will send enough packets to use up ACL LE credits and to fill the
245   // queue. And then send one more to verify we get an unavailable.
246   const uint16_t kAclLeCredits = 2;
247   const uint16_t kExpectedSuccessfulWrites =
248       kAclLeCredits + kL2capQueueCapacity;
249   // Set plenty of kL2capTxCredits to ensure that isn't the bottleneck.
250   const uint16_t kL2capTxCredits = kExpectedSuccessfulWrites + 1;
251 
252   struct {
253     int write_available_events = 0;
254   } capture;
255 
256   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
257       []([[maybe_unused]] H4PacketWithHci&& packet) {});
258   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
259       []([[maybe_unused]] H4PacketWithH4&& packet) {});
260 
261   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
262                               std::move(send_to_controller_fn),
263                               /*le_acl_credits_to_reserve=*/kAclLeCredits,
264                               /*br_edr_acl_credits_to_reserve=*/0);
265   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
266       proxy,
267       /*num_credits_to_reserve=*/kAclLeCredits));
268 
269   ChannelEventCallback&& kEventFn{[&capture](L2capChannelEvent event) {
270     if (event == L2capChannelEvent::kWriteAvailable) {
271       capture.write_available_events++;
272     }
273   }};
274   L2capCoc channel = BuildCoc(proxy,
275                               CocParameters{.handle = kHandle,
276                                             .tx_credits = kL2capTxCredits,
277                                             .event_fn = std::move(kEventFn)});
278 
279   // Use up the ACL credits and fill up the send queue.
280   for (int i = 0; i < kExpectedSuccessfulWrites; ++i) {
281     EXPECT_EQ(channel.IsWriteAvailable(), PW_STATUS_OK);
282     EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_OK);
283   }
284   EXPECT_EQ(0, capture.write_available_events);
285 
286   // Send queue is full, so Write should get unavailable.
287   EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_UNAVAILABLE);
288 
289   // Release a ACL credit, so even should trigger and write should be available
290   // again.
291   EXPECT_EQ(0, capture.write_available_events);
292   PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
293       proxy, FlatMap<uint16_t, uint16_t, 1>({{{kHandle, 1}}})));
294   EXPECT_EQ(1, capture.write_available_events);
295   EXPECT_EQ(channel.IsWriteAvailable(), PW_STATUS_OK);
296   EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_OK);
297 
298   // Verify event on just IsWriteAvailable
299   EXPECT_EQ(channel.IsWriteAvailable(), PW_STATUS_UNAVAILABLE);
300   PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
301       proxy, FlatMap<uint16_t, uint16_t, 1>({{{kHandle, 1}}})));
302   EXPECT_EQ(2, capture.write_available_events);
303 }
304 
305 // Verify we get unavailable when send queue is full due to running out of L2cap
306 // CoC credit.
307 // TODO: https://pwbug.dev/380299794 - Add equivalent test for other channel
308 // types (where appropriate).
TEST_F(L2capCocWriteTest,UnavailableWhenSendQueueIsFullDueToL2capCocCredits)309 TEST_F(L2capCocWriteTest, UnavailableWhenSendQueueIsFullDueToL2capCocCredits) {
310   // Should align with L2capChannel::kQueueCapacity.
311   static constexpr size_t kL2capQueueCapacity = 5;
312   // We will send enough packets to use up L2CAP CoC credits and to fill the
313   // queue. And then send one more to verify we get an unavailable.
314   const uint16_t kL2capTxCredits = 2;
315   const uint16_t kExpectedSuccessfulWrites =
316       kL2capTxCredits + kL2capQueueCapacity;
317   // Set plenty of kAclLeCredits to ensure that isn't the bottleneck.
318   const uint16_t kAclLeCredits = kExpectedSuccessfulWrites + 1;
319 
320   struct {
321     int write_available_events = 0;
322   } capture;
323 
324   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
325       []([[maybe_unused]] H4PacketWithHci&& packet) {});
326   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
327       []([[maybe_unused]] H4PacketWithH4&& packet) {});
328 
329   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
330                               std::move(send_to_controller_fn),
331                               /*le_acl_credits_to_reserve=*/kAclLeCredits,
332                               /*br_edr_acl_credits_to_reserve=*/0);
333   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
334       proxy,
335       /*num_credits_to_reserve=*/kAclLeCredits));
336 
337   ChannelEventCallback&& kEventFn{[&capture](L2capChannelEvent event) {
338     if (event == L2capChannelEvent::kWriteAvailable) {
339       capture.write_available_events++;
340     }
341   }};
342   L2capCoc channel = BuildCoc(proxy,
343                               CocParameters{.tx_credits = kL2capTxCredits,
344                                             .event_fn = std::move(kEventFn)});
345 
346   // Use up the CoC credits and fill up the send queue.
347   for (int i = 0; i < kExpectedSuccessfulWrites; ++i) {
348     EXPECT_EQ(channel.IsWriteAvailable(), PW_STATUS_OK);
349     EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_OK);
350   }
351   EXPECT_EQ(0, capture.write_available_events);
352 
353   // Send queue is full, so client should now get unavailable.
354   EXPECT_EQ(channel.IsWriteAvailable(), PW_STATUS_UNAVAILABLE);
355   EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_UNAVAILABLE);
356   EXPECT_EQ(0, capture.write_available_events);
357 
358   // TODO: https://pwbug.dev/380299794 - Verify we properly show available once
359   // Write is available again.
360 }
361 
TEST_F(L2capCocWriteTest,MultipleWritesMultipleChannels)362 TEST_F(L2capCocWriteTest, MultipleWritesMultipleChannels) {
363   struct {
364     int sends_called = 0;
365     std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
366   } capture;
367 
368   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
369       []([[maybe_unused]] H4PacketWithHci&& packet) {});
370   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
371       [&capture](H4PacketWithH4&& packet) {
372         ++capture.sends_called;
373         PW_TEST_ASSERT_OK_AND_ASSIGN(
374             auto acl,
375             MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
376         emboss::FirstKFrameView kframe = emboss::MakeFirstKFrameView(
377             acl.payload().BackingStorage().data(), acl.SizeInBytes());
378         for (size_t i = 0; i < 3; ++i) {
379           EXPECT_EQ(kframe.payload()[i].Read(), capture.payload[i]);
380         }
381       });
382 
383   constexpr uint16_t kNumChannels = 5;
384   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
385                               std::move(send_to_controller_fn),
386                               /*le_acl_credits_to_reserve=*/kNumChannels,
387                               /*br_edr_acl_credits_to_reserve=*/0);
388   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
389       proxy,
390       /*num_credits_to_reserve=*/kNumChannels));
391 
392   uint16_t remote_cid = 123;
393   std::array<L2capCoc, kNumChannels> channels{
394       BuildCoc(proxy, CocParameters{.remote_cid = remote_cid}),
395       BuildCoc(
396           proxy,
397           CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 1)}),
398       BuildCoc(
399           proxy,
400           CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 2)}),
401       BuildCoc(
402           proxy,
403           CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 3)}),
404       BuildCoc(
405           proxy,
406           CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 4)}),
407   };
408 
409   for (int i = 0; i < kNumChannels; ++i) {
410     PW_TEST_EXPECT_OK(
411         channels[i].Write(MultiBufFromSpan(span(capture.payload))).status);
412     std::for_each(capture.payload.begin(),
413                   capture.payload.end(),
414                   [](uint8_t& byte) { ++byte; });
415   }
416 
417   EXPECT_EQ(capture.sends_called, kNumChannels);
418 }
419 
420 // ########## L2capCocReadTest
421 
422 class L2capCocReadTest : public ProxyHostTest {};
423 
TEST_F(L2capCocReadTest,BasicRead)424 TEST_F(L2capCocReadTest, BasicRead) {
425   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
426       []([[maybe_unused]] H4PacketWithHci&& packet) {});
427   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
428       []([[maybe_unused]] H4PacketWithH4&& packet) {});
429   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
430                               std::move(send_to_controller_fn),
431                               /*le_acl_credits_to_reserve=*/0,
432                               /*br_edr_acl_credits_to_reserve=*/0);
433 
434   struct {
435     int receives_called = 0;
436     std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
437   } capture;
438 
439   uint16_t handle = 123;
440   uint16_t local_cid = 234;
441   L2capCoc channel = BuildCoc(
442       proxy,
443       CocParameters{.handle = handle,
444                     .local_cid = local_cid,
445                     .receive_fn = [&capture](multibuf::MultiBuf&& payload) {
446                       ++capture.receives_called;
447                       std::optional<ConstByteSpan> rx_sdu =
448                           payload.ContiguousSpan();
449                       ASSERT_TRUE(rx_sdu);
450                       ConstByteSpan expected_sdu =
451                           as_bytes(span(capture.expected_payload.data(),
452                                         capture.expected_payload.size()));
453                       EXPECT_TRUE(std::equal(rx_sdu->begin(),
454                                              rx_sdu->end(),
455                                              expected_sdu.begin(),
456                                              expected_sdu.end()));
457                     }});
458 
459   std::array<uint8_t,
460              kFirstKFrameOverAclMinSize + capture.expected_payload.size()>
461       hci_arr;
462   hci_arr.fill(0);
463   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
464 
465   Result<emboss::AclDataFrameWriter> acl =
466       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
467   acl->header().handle().Write(handle);
468   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
469                                  capture.expected_payload.size());
470 
471   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
472       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
473   kframe.pdu_length().Write(kSduLengthFieldSize +
474                             capture.expected_payload.size());
475   kframe.channel_id().Write(local_cid);
476   kframe.sdu_length().Write(capture.expected_payload.size());
477   std::copy(capture.expected_payload.begin(),
478             capture.expected_payload.end(),
479             hci_arr.begin() + kFirstKFrameOverAclMinSize);
480 
481   // Send ACL data packet destined for the CoC we registered.
482   proxy.HandleH4HciFromController(std::move(h4_packet));
483 
484   EXPECT_EQ(capture.receives_called, 1);
485 }
486 
TEST_F(L2capCocReadTest,RxCreditsAreReplenished)487 TEST_F(L2capCocReadTest, RxCreditsAreReplenished) {
488   const uint16_t kRxCredits = 10;
489   // Corresponds to kRxCreditReplenishThreshold in l2cap_coc.cc times
490   // kRxCredits.
491   // TODO: b/353734827 - Update test once client can to determine this constant.
492   const uint16_t kRxThreshold = 3;
493   struct {
494     uint16_t handle = 123;
495     uint16_t local_cid = 234;
496     uint16_t tx_packets_sent = 0;
497     // We expect when we reach threshold to replenish exactly that amount.
498     uint16_t expected_additional_credits = kRxThreshold;
499   } capture;
500 
501   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
502       []([[maybe_unused]] H4PacketWithHci&& packet) {});
503   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
504       [&capture]([[maybe_unused]] H4PacketWithH4&& packet) {
505         capture.tx_packets_sent++;
506 
507         // Verify packet is properly formed FLOW_CONTROL_CREDIT_IND with the
508         // expected credits.
509         PW_TEST_ASSERT_OK_AND_ASSIGN(
510             auto acl,
511             MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
512         EXPECT_EQ(acl.header().handle().Read(), capture.handle);
513         EXPECT_EQ(
514             acl.data_total_length().Read(),
515             emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
516                 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
517         emboss::CFrameView cframe = emboss::MakeCFrameView(
518             acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
519         EXPECT_EQ(cframe.pdu_length().Read(),
520                   emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
521         // 0x0005 = LE-U fixed signaling channel ID.
522         EXPECT_EQ(cframe.channel_id().Read(), 0x0005);
523         emboss::L2capFlowControlCreditIndView ind =
524             emboss::MakeL2capFlowControlCreditIndView(
525                 cframe.payload().BackingStorage().data(),
526                 cframe.payload().SizeInBytes());
527         EXPECT_EQ(ind.command_header().code().Read(),
528                   emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
529         EXPECT_EQ(
530             ind.command_header().data_length().Read(),
531             emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
532                 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
533         EXPECT_EQ(ind.cid().Read(), capture.local_cid);
534         EXPECT_EQ(ind.credits().Read(), capture.expected_additional_credits);
535       });
536   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
537                               std::move(send_to_controller_fn),
538                               /*le_acl_credits_to_reserve=*/10,
539                               /*br_edr_acl_credits_to_reserve=*/0);
540   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 12));
541 
542   L2capCoc channel = BuildCoc(proxy,
543                               CocParameters{.handle = capture.handle,
544                                             .local_cid = capture.local_cid,
545                                             .rx_credits = kRxCredits});
546 
547   auto SendRxH4Packet = [&capture, &proxy]() {
548     std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
549     std::array<uint8_t, kFirstKFrameOverAclMinSize + expected_payload.size()>
550         hci_arr;
551     hci_arr.fill(0);
552     H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
553 
554     Result<emboss::AclDataFrameWriter> acl =
555         MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
556     acl->header().handle().Write(capture.handle);
557     acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
558                                    expected_payload.size());
559 
560     emboss::FirstKFrameWriter kframe =
561         emboss::MakeFirstKFrameView(acl->payload().BackingStorage().data(),
562                                     acl->data_total_length().Read());
563     kframe.pdu_length().Write(kSduLengthFieldSize + expected_payload.size());
564     kframe.channel_id().Write(capture.local_cid);
565     kframe.sdu_length().Write(expected_payload.size());
566     std::copy(expected_payload.begin(),
567               expected_payload.end(),
568               hci_arr.begin() + kFirstKFrameOverAclMinSize);
569 
570     proxy.HandleH4HciFromController(std::move(h4_packet));
571   };
572 
573   // Rx packets before threshold should not trigger a credit packet.
574   EXPECT_EQ(0, capture.tx_packets_sent);
575   for (int i = 0; i < kRxThreshold - 1; i++) {
576     // Send ACL data packet destined for the CoC we registered.
577     SendRxH4Packet();
578     EXPECT_EQ(0, capture.tx_packets_sent);
579   }
580 
581   // RX packet at threshold should trigger exactly one credit packet with
582   // threshold credits.
583   SendRxH4Packet();
584   EXPECT_EQ(1, capture.tx_packets_sent);
585 
586   // Send just up to threshold again.
587   for (int i = 0; i < kRxThreshold - 1; i++) {
588     SendRxH4Packet();
589     EXPECT_EQ(1, capture.tx_packets_sent);
590   }
591 
592   // RX packet at threshold should once again trigger exactly one credit packet
593   // with threshold credits.
594   SendRxH4Packet();
595   EXPECT_EQ(2, capture.tx_packets_sent);
596 }
597 
TEST_F(L2capCocReadTest,ChannelHandlesReadWithNullReceiveFn)598 TEST_F(L2capCocReadTest, ChannelHandlesReadWithNullReceiveFn) {
599   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
600       []([[maybe_unused]] H4PacketWithHci&& packet) { FAIL(); });
601   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
602       []([[maybe_unused]] H4PacketWithH4&& packet) {});
603   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
604                               std::move(send_to_controller_fn),
605                               /*le_acl_credits_to_reserve=*/0,
606                               /*br_edr_acl_credits_to_reserve=*/0);
607 
608   uint16_t handle = 123;
609   uint16_t local_cid = 234;
610   L2capCoc channel = BuildCoc(
611       proxy,
612       CocParameters{.handle = handle,
613                     .local_cid = local_cid,
614                     .rx_credits = 1,
615                     .event_fn = []([[maybe_unused]] L2capChannelEvent event) {
616                       FAIL();
617                     }});
618 
619   std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
620   hci_arr.fill(0);
621   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
622 
623   Result<emboss::AclDataFrameWriter> acl =
624       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
625   acl->header().handle().Write(handle);
626   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
627 
628   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
629       acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
630   kframe.pdu_length().Write(kSduLengthFieldSize);
631   kframe.channel_id().Write(local_cid);
632   kframe.sdu_length().Write(0);
633 
634   proxy.HandleH4HciFromController(std::move(h4_packet));
635 }
636 
TEST_F(L2capCocReadTest,ErrorOnRxToStoppedChannel)637 TEST_F(L2capCocReadTest, ErrorOnRxToStoppedChannel) {
638   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
639       []([[maybe_unused]] H4PacketWithHci&& packet) {});
640   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
641       []([[maybe_unused]] H4PacketWithH4&& packet) {});
642   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
643                               std::move(send_to_controller_fn),
644                               /*le_acl_credits_to_reserve=*/0,
645                               /*br_edr_acl_credits_to_reserve*/ 0);
646 
647   int events_received = 0;
648   uint16_t num_invalid_rx = 3;
649   uint16_t handle = 123;
650   uint16_t local_cid = 234;
651   L2capCoc channel = BuildCoc(
652       proxy,
653       CocParameters{.handle = handle,
654                     .local_cid = local_cid,
655                     .rx_credits = num_invalid_rx,
656                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); },
657                     .event_fn =
658                         [&events_received](L2capChannelEvent event) {
659                           ++events_received;
660                           EXPECT_EQ(event, L2capChannelEvent::kRxWhileStopped);
661                         }});
662 
663   std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
664   hci_arr.fill(0);
665 
666   Result<emboss::AclDataFrameWriter> acl =
667       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
668   acl->header().handle().Write(handle);
669   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
670 
671   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
672       acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
673   kframe.pdu_length().Write(kSduLengthFieldSize);
674   kframe.channel_id().Write(local_cid);
675   kframe.sdu_length().Write(0);
676 
677   channel.Stop();
678   for (int i = 0; i < num_invalid_rx; ++i) {
679     H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
680     proxy.HandleH4HciFromController(std::move(h4_packet));
681   }
682   EXPECT_EQ(events_received, num_invalid_rx);
683 }
684 
TEST_F(L2capCocReadTest,TooShortAclPassedToHost)685 TEST_F(L2capCocReadTest, TooShortAclPassedToHost) {
686   int sends_called = 0;
687   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
688       [&sends_called]([[maybe_unused]] H4PacketWithHci&& packet) {
689         ++sends_called;
690       });
691   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
692       []([[maybe_unused]] H4PacketWithH4&& packet) {});
693   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
694                               std::move(send_to_controller_fn),
695                               /*le_acl_credits_to_reserve=*/0,
696                               /*br_edr_acl_credits_to_reserve=*/0);
697 
698   uint16_t handle = 123;
699   uint16_t local_cid = 234;
700   L2capCoc channel = BuildCoc(
701       proxy,
702       CocParameters{.handle = handle,
703                     .local_cid = local_cid,
704                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); }});
705 
706   std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
707   hci_arr.fill(0);
708   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
709 
710   Result<emboss::AclDataFrameWriter> acl =
711       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
712   acl->header().handle().Write(handle);
713   // Write size larger than buffer size.
714   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() + 5);
715 
716   proxy.HandleH4HciFromController(std::move(h4_packet));
717 
718   EXPECT_EQ(sends_called, 1);
719 }
720 
TEST_F(L2capCocReadTest,ChannelClosedWithErrorIfMtuExceeded)721 TEST_F(L2capCocReadTest, ChannelClosedWithErrorIfMtuExceeded) {
722   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
723       []([[maybe_unused]] H4PacketWithHci&& packet) {});
724   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
725       []([[maybe_unused]] H4PacketWithH4&& packet) {});
726   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
727                               std::move(send_to_controller_fn),
728                               /*le_acl_credits_to_reserve=*/0,
729                               /*br_edr_acl_credits_to_reserve=*/0);
730 
731   uint16_t handle = 123;
732   uint16_t local_cid = 234;
733   constexpr uint16_t kRxMtu = 5;
734   int events_received = 0;
735   L2capCoc channel = BuildCoc(
736       proxy,
737       CocParameters{.handle = handle,
738                     .local_cid = local_cid,
739                     .rx_mtu = kRxMtu,
740                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); },
741                     .event_fn =
742                         [&events_received](L2capChannelEvent event) {
743                           ++events_received;
744                           EXPECT_EQ(event, L2capChannelEvent::kRxInvalid);
745                         }});
746 
747   constexpr uint16_t kPayloadSize = kRxMtu + 1;
748   std::array<uint8_t, kFirstKFrameOverAclMinSize + kPayloadSize> hci_arr;
749   hci_arr.fill(0);
750   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
751 
752   Result<emboss::AclDataFrameWriter> acl =
753       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
754   acl->header().handle().Write(handle);
755   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
756                                  kPayloadSize);
757 
758   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
759       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
760   kframe.pdu_length().Write(kSduLengthFieldSize + kPayloadSize);
761   kframe.channel_id().Write(local_cid);
762   kframe.sdu_length().Write(kPayloadSize);
763 
764   proxy.HandleH4HciFromController(std::move(h4_packet));
765 
766   EXPECT_EQ(events_received, 1);
767 }
768 
TEST_F(L2capCocReadTest,ChannelClosedWithErrorIfMpsExceeded)769 TEST_F(L2capCocReadTest, ChannelClosedWithErrorIfMpsExceeded) {
770   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
771       []([[maybe_unused]] H4PacketWithHci&& packet) {});
772   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
773       []([[maybe_unused]] H4PacketWithH4&& packet) {});
774   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
775                               std::move(send_to_controller_fn),
776                               /*le_acl_credits_to_reserve=*/0,
777                               /*br_edr_acl_credits_to_reserve=*/0);
778 
779   uint16_t handle = 123;
780   uint16_t local_cid = 234;
781   constexpr uint16_t kRxMps = 5;
782   int events_received = 0;
783   L2capCoc channel = BuildCoc(
784       proxy,
785       CocParameters{.handle = handle,
786                     .local_cid = local_cid,
787                     .rx_mps = kRxMps,
788                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); },
789                     .event_fn =
790                         [&events_received](L2capChannelEvent event) {
791                           ++events_received;
792                           EXPECT_EQ(event, L2capChannelEvent::kRxInvalid);
793                         }});
794 
795   constexpr uint16_t kPayloadSize = kRxMps + 1;
796   std::array<uint8_t, kFirstKFrameOverAclMinSize + kPayloadSize> hci_arr;
797   hci_arr.fill(0);
798   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
799 
800   Result<emboss::AclDataFrameWriter> acl =
801       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
802   acl->header().handle().Write(handle);
803   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
804                                  kPayloadSize);
805 
806   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
807       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
808   kframe.pdu_length().Write(kSduLengthFieldSize + kPayloadSize);
809   kframe.channel_id().Write(local_cid);
810   kframe.sdu_length().Write(kPayloadSize);
811 
812   proxy.HandleH4HciFromController(std::move(h4_packet));
813 
814   EXPECT_EQ(events_received, 1);
815 }
816 
TEST_F(L2capCocReadTest,ChannelClosedWithErrorIfPayloadsExceedSduLength)817 TEST_F(L2capCocReadTest, ChannelClosedWithErrorIfPayloadsExceedSduLength) {
818   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
819       []([[maybe_unused]] H4PacketWithHci&& packet) {});
820   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
821       []([[maybe_unused]] H4PacketWithH4&& packet) {});
822   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
823                               std::move(send_to_controller_fn),
824                               /*le_acl_credits_to_reserve=*/0,
825                               /*br_edr_acl_credits_to_reserve=*/0);
826 
827   uint16_t handle = 123;
828   uint16_t local_cid = 234;
829   int events_received = 0;
830   L2capCoc channel = BuildCoc(
831       proxy,
832       CocParameters{.handle = handle,
833                     .local_cid = local_cid,
834                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); },
835                     .event_fn =
836                         [&events_received](L2capChannelEvent event) {
837                           ++events_received;
838                           EXPECT_EQ(event, L2capChannelEvent::kRxInvalid);
839                         }});
840 
841   constexpr uint16_t k1stPayloadSize = 1;
842   constexpr uint16_t k2ndPayloadSize = 3;
843   ASSERT_GT(k2ndPayloadSize, k1stPayloadSize + 1);
844   // Indicate SDU length that does not account for the 2nd payload size.
845   constexpr uint16_t kSduLength = k1stPayloadSize + 1;
846 
847   std::array<uint8_t,
848              kFirstKFrameOverAclMinSize +
849                  std::max(k1stPayloadSize, k2ndPayloadSize)>
850       hci_arr;
851   hci_arr.fill(0);
852   H4PacketWithHci h4_1st_segment{
853       emboss::H4PacketType::ACL_DATA,
854       pw::span<uint8_t>(hci_arr.data(),
855                         kFirstKFrameOverAclMinSize + k1stPayloadSize)};
856 
857   Result<emboss::AclDataFrameWriter> acl =
858       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
859   acl->header().handle().Write(handle);
860   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
861                                  k1stPayloadSize);
862 
863   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
864       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
865   kframe.pdu_length().Write(kSduLengthFieldSize + k1stPayloadSize);
866   kframe.channel_id().Write(local_cid);
867   kframe.sdu_length().Write(kSduLength);
868 
869   proxy.HandleH4HciFromController(std::move(h4_1st_segment));
870 
871   // Send 2nd segment.
872   acl->data_total_length().Write(emboss::SubsequentKFrame::MinSizeInBytes() +
873                                  k2ndPayloadSize);
874   kframe.pdu_length().Write(k2ndPayloadSize);
875   H4PacketWithHci h4_2nd_segment{
876       emboss::H4PacketType::ACL_DATA,
877       pw::span<uint8_t>(hci_arr.data(),
878                         emboss::AclDataFrame::MinSizeInBytes() +
879                             emboss::SubsequentKFrame::MinSizeInBytes() +
880                             k2ndPayloadSize)};
881 
882   proxy.HandleH4HciFromController(std::move(h4_2nd_segment));
883 
884   EXPECT_EQ(events_received, 1);
885 }
886 
TEST_F(L2capCocReadTest,NoReadOnStoppedChannel)887 TEST_F(L2capCocReadTest, NoReadOnStoppedChannel) {
888   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
889       []([[maybe_unused]] H4PacketWithHci&& packet) {});
890   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
891       []([[maybe_unused]] H4PacketWithH4&& packet) {});
892   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
893                               std::move(send_to_controller_fn),
894                               /*le_acl_credits_to_reserve=*/0,
895                               /*br_edr_acl_credits_to_reserve=*/0);
896 
897   uint16_t handle = 123;
898   uint16_t local_cid = 234;
899   L2capCoc channel = BuildCoc(
900       proxy,
901       CocParameters{.handle = handle,
902                     .local_cid = local_cid,
903                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); }});
904 
905   std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
906   hci_arr.fill(0);
907   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
908 
909   Result<emboss::AclDataFrameWriter> acl =
910       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
911   acl->header().handle().Write(handle);
912   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
913 
914   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
915       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
916   kframe.pdu_length().Write(kSduLengthFieldSize);
917   kframe.channel_id().Write(local_cid);
918 
919   channel.Stop();
920   proxy.HandleH4HciFromController(std::move(h4_packet));
921 }
922 
TEST_F(L2capCocReadTest,NoReadOnSameCidDifferentConnectionHandle)923 TEST_F(L2capCocReadTest, NoReadOnSameCidDifferentConnectionHandle) {
924   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
925       []([[maybe_unused]] H4PacketWithHci&& packet) {});
926   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
927       []([[maybe_unused]] H4PacketWithH4&& packet) {});
928   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
929                               std::move(send_to_controller_fn),
930                               /*le_acl_credits_to_reserve=*/0,
931                               /*br_edr_acl_credits_to_reserve=*/0);
932 
933   uint16_t local_cid = 234;
934   L2capCoc channel = BuildCoc(
935       proxy,
936       CocParameters{.local_cid = local_cid,
937                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); }});
938 
939   std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
940   hci_arr.fill(0);
941   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
942 
943   Result<emboss::AclDataFrameWriter> acl =
944       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
945   acl->header().handle().Write(444);
946   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
947 
948   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
949       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
950   kframe.pdu_length().Write(kSduLengthFieldSize);
951   kframe.channel_id().Write(local_cid);
952 
953   proxy.HandleH4HciFromController(std::move(h4_packet));
954 }
955 
TEST_F(L2capCocReadTest,MultipleReadsSameChannel)956 TEST_F(L2capCocReadTest, MultipleReadsSameChannel) {
957   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
958       []([[maybe_unused]] H4PacketWithHci&& packet) {});
959   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
960       []([[maybe_unused]] H4PacketWithH4&& packet) {});
961   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
962                               std::move(send_to_controller_fn),
963                               /*le_acl_credits_to_reserve=*/0,
964                               /*br_edr_acl_credits_to_reserve=*/0);
965 
966   struct {
967     int sends_called = 0;
968     std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
969   } capture;
970 
971   uint16_t handle = 123;
972   uint16_t local_cid = 234;
973   L2capCoc channel = BuildCoc(
974       proxy,
975       CocParameters{.handle = handle,
976                     .local_cid = local_cid,
977                     .receive_fn = [&capture](multibuf::MultiBuf&& payload) {
978                       ++capture.sends_called;
979                       std::optional<ConstByteSpan> rx_sdu =
980                           payload.ContiguousSpan();
981                       ASSERT_TRUE(rx_sdu);
982                       ConstByteSpan expected_sdu = as_bytes(
983                           span(capture.payload.data(), capture.payload.size()));
984                       EXPECT_TRUE(std::equal(rx_sdu->begin(),
985                                              rx_sdu->end(),
986                                              expected_sdu.begin(),
987                                              expected_sdu.end()));
988                     }});
989 
990   std::array<uint8_t, kFirstKFrameOverAclMinSize + capture.payload.size()>
991       hci_arr;
992   hci_arr.fill(0);
993 
994   Result<emboss::AclDataFrameWriter> acl =
995       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
996   acl->header().handle().Write(handle);
997   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
998                                  capture.payload.size());
999 
1000   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
1001       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
1002   kframe.pdu_length().Write(capture.payload.size() + kSduLengthFieldSize);
1003   kframe.channel_id().Write(local_cid);
1004   kframe.sdu_length().Write(capture.payload.size());
1005 
1006   int num_reads = 10;
1007   for (int i = 0; i < num_reads; ++i) {
1008     std::copy(capture.payload.begin(),
1009               capture.payload.end(),
1010               hci_arr.begin() + kFirstKFrameOverAclMinSize);
1011 
1012     H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1013     proxy.HandleH4HciFromController(std::move(h4_packet));
1014 
1015     std::for_each(capture.payload.begin(),
1016                   capture.payload.end(),
1017                   [](uint8_t& byte) { ++byte; });
1018   }
1019 
1020   EXPECT_EQ(capture.sends_called, num_reads);
1021 }
1022 
TEST_F(L2capCocReadTest,MultipleReadsMultipleChannels)1023 TEST_F(L2capCocReadTest, MultipleReadsMultipleChannels) {
1024   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1025       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1026   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1027       []([[maybe_unused]] H4PacketWithH4&& packet) {});
1028   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1029                               std::move(send_to_controller_fn),
1030                               /*le_acl_credits_to_reserve=*/0,
1031                               /*br_edr_acl_credits_to_reserve=*/0);
1032 
1033   struct {
1034     int sends_called = 0;
1035     std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
1036   } capture;
1037 
1038   constexpr int kNumChannels = 5;
1039   uint16_t local_cid = 123;
1040   uint16_t handle = 456;
1041   auto receive_fn = [&capture](multibuf::MultiBuf&& payload) {
1042     ++capture.sends_called;
1043     std::optional<ConstByteSpan> rx_sdu = payload.ContiguousSpan();
1044     ASSERT_TRUE(rx_sdu);
1045     ConstByteSpan expected_sdu =
1046         as_bytes(span(capture.payload.data(), capture.payload.size()));
1047     EXPECT_TRUE(std::equal(rx_sdu->begin(),
1048                            rx_sdu->end(),
1049                            expected_sdu.begin(),
1050                            expected_sdu.end()));
1051   };
1052   std::array<L2capCoc, kNumChannels> channels{
1053       BuildCoc(proxy,
1054                CocParameters{.handle = handle,
1055                              .local_cid = local_cid,
1056                              .receive_fn = receive_fn}),
1057       BuildCoc(proxy,
1058                CocParameters{.handle = handle,
1059                              .local_cid = static_cast<uint16_t>(local_cid + 1),
1060                              .receive_fn = receive_fn}),
1061       BuildCoc(proxy,
1062                CocParameters{.handle = handle,
1063                              .local_cid = static_cast<uint16_t>(local_cid + 2),
1064                              .receive_fn = receive_fn}),
1065       BuildCoc(proxy,
1066                CocParameters{.handle = handle,
1067                              .local_cid = static_cast<uint16_t>(local_cid + 3),
1068                              .receive_fn = receive_fn}),
1069       BuildCoc(proxy,
1070                CocParameters{.handle = handle,
1071                              .local_cid = static_cast<uint16_t>(local_cid + 4),
1072                              .receive_fn = receive_fn}),
1073   };
1074 
1075   std::array<uint8_t, kFirstKFrameOverAclMinSize + capture.payload.size()>
1076       hci_arr;
1077   hci_arr.fill(0);
1078 
1079   Result<emboss::AclDataFrameWriter> acl =
1080       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
1081   acl->header().handle().Write(handle);
1082   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
1083                                  capture.payload.size());
1084 
1085   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
1086       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
1087   kframe.pdu_length().Write(capture.payload.size() + kSduLengthFieldSize);
1088   kframe.sdu_length().Write(capture.payload.size());
1089 
1090   for (int i = 0; i < kNumChannels; ++i) {
1091     kframe.channel_id().Write(local_cid + i);
1092 
1093     std::copy(capture.payload.begin(),
1094               capture.payload.end(),
1095               hci_arr.begin() + kFirstKFrameOverAclMinSize);
1096 
1097     H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1098     proxy.HandleH4HciFromController(std::move(h4_packet));
1099 
1100     std::for_each(capture.payload.begin(),
1101                   capture.payload.end(),
1102                   [](uint8_t& byte) { ++byte; });
1103   }
1104 
1105   EXPECT_EQ(capture.sends_called, kNumChannels);
1106 }
1107 
TEST_F(L2capCocReadTest,ChannelStoppageDoNotAffectOtherChannels)1108 TEST_F(L2capCocReadTest, ChannelStoppageDoNotAffectOtherChannels) {
1109   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1110       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1111   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1112       []([[maybe_unused]] H4PacketWithH4&& packet) {});
1113   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1114                               std::move(send_to_controller_fn),
1115                               /*le_acl_credits_to_reserve=*/0,
1116                               /*br_edr_acl_credits_to_reserve=*/0);
1117 
1118   struct {
1119     int sends_called = 0;
1120     std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
1121   } capture;
1122 
1123   constexpr int kNumChannels = 5;
1124   uint16_t local_cid = 123;
1125   uint16_t handle = 456;
1126   auto receive_fn = [&capture](multibuf::MultiBuf&& payload) {
1127     ++capture.sends_called;
1128     std::optional<ConstByteSpan> rx_sdu = payload.ContiguousSpan();
1129     ASSERT_TRUE(rx_sdu);
1130     ConstByteSpan expected_sdu =
1131         as_bytes(span(capture.payload.data(), capture.payload.size()));
1132     EXPECT_TRUE(std::equal(rx_sdu->begin(),
1133                            rx_sdu->end(),
1134                            expected_sdu.begin(),
1135                            expected_sdu.end()));
1136   };
1137   std::array<L2capCoc, kNumChannels> channels{
1138       BuildCoc(proxy,
1139                CocParameters{.handle = handle,
1140                              .local_cid = local_cid,
1141                              .receive_fn = receive_fn}),
1142       BuildCoc(proxy,
1143                CocParameters{.handle = handle,
1144                              .local_cid = static_cast<uint16_t>(local_cid + 1),
1145                              .receive_fn = receive_fn}),
1146       BuildCoc(proxy,
1147                CocParameters{.handle = handle,
1148                              .local_cid = static_cast<uint16_t>(local_cid + 2),
1149                              .receive_fn = receive_fn}),
1150       BuildCoc(proxy,
1151                CocParameters{.handle = handle,
1152                              .local_cid = static_cast<uint16_t>(local_cid + 3),
1153                              .receive_fn = receive_fn}),
1154       BuildCoc(proxy,
1155                CocParameters{.handle = handle,
1156                              .local_cid = static_cast<uint16_t>(local_cid + 4),
1157                              .receive_fn = receive_fn}),
1158   };
1159 
1160   // Stop the 2nd and 4th of the 5 channels.
1161   channels[1].Stop();
1162   channels[3].Stop();
1163 
1164   std::array<uint8_t, kFirstKFrameOverAclMinSize + capture.payload.size()>
1165       hci_arr;
1166   hci_arr.fill(0);
1167 
1168   Result<emboss::AclDataFrameWriter> acl =
1169       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
1170   acl->header().handle().Write(handle);
1171   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
1172                                  capture.payload.size());
1173 
1174   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
1175       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
1176   kframe.pdu_length().Write(capture.payload.size() + kSduLengthFieldSize);
1177   kframe.sdu_length().Write(capture.payload.size());
1178 
1179   for (int i = 0; i < kNumChannels; ++i) {
1180     // Still send packets to the stopped channels, so we can validate that it
1181     // does not cause issues.
1182     kframe.channel_id().Write(local_cid + i);
1183 
1184     std::copy(capture.payload.begin(),
1185               capture.payload.end(),
1186               hci_arr.begin() + kFirstKFrameOverAclMinSize);
1187 
1188     H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1189     proxy.HandleH4HciFromController(std::move(h4_packet));
1190 
1191     std::for_each(capture.payload.begin(),
1192                   capture.payload.end(),
1193                   [](uint8_t& byte) { ++byte; });
1194   }
1195 
1196   EXPECT_EQ(capture.sends_called, kNumChannels - 2);
1197 }
1198 
TEST_F(L2capCocReadTest,NonCocAclPacketPassesThroughToHost)1199 TEST_F(L2capCocReadTest, NonCocAclPacketPassesThroughToHost) {
1200   struct {
1201     int sends_called = 0;
1202     uint16_t handle = 123;
1203     std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
1204   } capture;
1205 
1206   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1207       [&capture](H4PacketWithHci&& packet) {
1208         ++capture.sends_called;
1209         Result<emboss::AclDataFrameWriter> acl =
1210             MakeEmbossWriter<emboss::AclDataFrameWriter>(packet.GetHciSpan());
1211         EXPECT_EQ(acl->header().handle().Read(), capture.handle);
1212         emboss::BFrameView bframe =
1213             emboss::MakeBFrameView(acl->payload().BackingStorage().data(),
1214                                    acl->data_total_length().Read());
1215         for (size_t i = 0; i < capture.expected_payload.size(); ++i) {
1216           EXPECT_EQ(bframe.payload()[i].Read(), capture.expected_payload[i]);
1217         }
1218       });
1219   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1220       []([[maybe_unused]] H4PacketWithH4&& packet) {});
1221   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1222                               std::move(send_to_controller_fn),
1223                               /*le_acl_credits_to_reserve=*/0,
1224                               /*br_edr_acl_credits_to_reserve=*/0);
1225 
1226   // Acquire unused CoC to validate that doing so does not interfere.
1227   L2capCoc channel = BuildCoc(
1228       proxy,
1229       CocParameters{.handle = capture.handle,
1230                     .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); }});
1231 
1232   std::array<uint8_t,
1233              emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
1234                  emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
1235                  capture.expected_payload.size()>
1236       hci_arr;
1237   hci_arr.fill(0);
1238   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1239 
1240   Result<emboss::AclDataFrameWriter> acl =
1241       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
1242   acl->header().handle().Write(capture.handle);
1243   acl->data_total_length().Write(
1244       emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
1245       capture.expected_payload.size());
1246 
1247   emboss::BFrameWriter bframe = emboss::MakeBFrameView(
1248       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
1249   bframe.pdu_length().Write(capture.expected_payload.size());
1250   bframe.channel_id().Write(111);
1251   std::copy(capture.expected_payload.begin(),
1252             capture.expected_payload.end(),
1253             hci_arr.begin() +
1254                 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
1255                 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
1256 
1257   // Send ACL packet that should be forwarded to host.
1258   proxy.HandleH4HciFromController(std::move(h4_packet));
1259 
1260   EXPECT_EQ(capture.sends_called, 1);
1261 }
1262 
TEST_F(L2capCocReadTest,AclFrameWithIncompleteL2capHeaderForwardedToHost)1263 TEST_F(L2capCocReadTest, AclFrameWithIncompleteL2capHeaderForwardedToHost) {
1264   int sends_to_host_called = 0;
1265   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1266       [&sends_to_host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
1267         ++sends_to_host_called;
1268       });
1269   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1270       []([[maybe_unused]] H4PacketWithH4&& packet) {});
1271   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1272                               std::move(send_to_controller_fn),
1273                               /*le_acl_credits_to_reserve=*/0,
1274                               /*br_edr_acl_credits_to_reserve=*/0);
1275 
1276   uint16_t handle = 123;
1277   L2capCoc channel = BuildCoc(proxy, CocParameters{.handle = handle});
1278 
1279   std::array<uint8_t, emboss::AclDataFrameHeader::IntrinsicSizeInBytes()>
1280       hci_arr;
1281   hci_arr.fill(0);
1282   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1283 
1284   Result<emboss::AclDataFrameWriter> acl =
1285       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
1286   acl->header().handle().Write(handle);
1287   acl->data_total_length().Write(0);
1288 
1289   proxy.HandleH4HciFromController(std::move(h4_packet));
1290 
1291   EXPECT_EQ(sends_to_host_called, 1);
1292 }
1293 
TEST_F(L2capCocReadTest,FragmentedPduDoesNotInterfereWithOtherChannels)1294 TEST_F(L2capCocReadTest, FragmentedPduDoesNotInterfereWithOtherChannels) {
1295   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1296       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1297   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1298       []([[maybe_unused]] H4PacketWithH4&& packet) {});
1299   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1300                               std::move(send_to_controller_fn),
1301                               /*le_acl_credits_to_reserve=*/0,
1302                               /*br_edr_acl_credits_to_reserve=*/0);
1303 
1304   uint16_t handle_frag = 0x123, handle_fine = 0x234;
1305   uint16_t cid_frag = 0x345, cid_fine = 0x456;
1306   int packets_received = 0;
1307   auto receive_fn = [&packets_received](multibuf::MultiBuf&&) {
1308     ++packets_received;
1309   };
1310   L2capCoc frag_channel = BuildCoc(proxy,
1311                                    CocParameters{
1312                                        .handle = handle_frag,
1313                                        .local_cid = cid_frag,
1314                                        .receive_fn = receive_fn,
1315                                    });
1316   L2capCoc fine_channel = BuildCoc(proxy,
1317                                    CocParameters{
1318                                        .handle = handle_fine,
1319                                        .local_cid = cid_fine,
1320                                        .receive_fn = receive_fn,
1321                                    });
1322 
1323   // Order of receptions:
1324   // 1. 1st of 3 fragments to frag_channel.
1325   // 2. Non-fragmented PDU to fine_channel.
1326   // 3. 2nd of 3 fragments to frag_channel.
1327   // 4. Non-fragmented PDU to fine_channel.
1328   // 5. 3rd of 3 fragments to frag_channel.
1329   // 6. Non-fragmented PDU to fine_channel.
1330 
1331   constexpr uint8_t kPduLength = 14;
1332   ASSERT_GT(kPduLength, kSduLengthFieldSize);
1333   constexpr uint8_t kSduLength = kPduLength - kSduLengthFieldSize;
1334 
1335   // 1. 1st of 3 fragments to frag_channel.
1336   std::array<uint8_t, emboss::AclDataFrame::MinSizeInBytes() + kSduLength>
1337       frag_hci_arr;
1338   frag_hci_arr.fill(0);
1339   H4PacketWithHci h4_1st_fragment{
1340       emboss::H4PacketType::ACL_DATA,
1341       pw::span(frag_hci_arr.data(), kFirstKFrameOverAclMinSize)};
1342 
1343   Result<emboss::AclDataFrameWriter> acl_frag =
1344       MakeEmbossWriter<emboss::AclDataFrameWriter>(frag_hci_arr);
1345   acl_frag->header().handle().Write(handle_frag);
1346   acl_frag->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
1347 
1348   emboss::FirstKFrameWriter kframe_frag =
1349       emboss::MakeFirstKFrameView(acl_frag->payload().BackingStorage().data(),
1350                                   acl_frag->data_total_length().Read());
1351   kframe_frag.pdu_length().Write(kPduLength);
1352   kframe_frag.channel_id().Write(cid_frag);
1353   kframe_frag.sdu_length().Write(kSduLength);
1354 
1355   proxy.HandleH4HciFromController(std::move(h4_1st_fragment));
1356 
1357   // 2. Non-fragmented PDU to fine_channel.
1358   std::array<uint8_t, kFirstKFrameOverAclMinSize> fine_hci_arr;
1359   fine_hci_arr.fill(0);
1360   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA,
1361                             pw::span(fine_hci_arr)};
1362 
1363   Result<emboss::AclDataFrameWriter> acl_fine =
1364       MakeEmbossWriter<emboss::AclDataFrameWriter>(fine_hci_arr);
1365   acl_fine->header().handle().Write(handle_fine);
1366   acl_fine->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
1367 
1368   emboss::FirstKFrameWriter kframe_fine =
1369       emboss::MakeFirstKFrameView(acl_fine->payload().BackingStorage().data(),
1370                                   acl_fine->data_total_length().Read());
1371   kframe_fine.pdu_length().Write(kSduLengthFieldSize);
1372   kframe_fine.channel_id().Write(cid_fine);
1373   kframe_fine.sdu_length().Write(0);
1374 
1375   proxy.HandleH4HciFromController(std::move(h4_packet));
1376 
1377   // 3. 2nd of 3 fragments to frag_channel.
1378   acl_frag->header().packet_boundary_flag().Write(
1379       emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT);
1380   acl_frag->data_total_length().Write(kSduLength / 2);
1381   H4PacketWithHci h4_2nd_fragment{
1382       emboss::H4PacketType::ACL_DATA,
1383       pw::span<uint8_t>(
1384           frag_hci_arr.data(),
1385           emboss::AclDataFrame::MinSizeInBytes() + kSduLength / 2)};
1386   proxy.HandleH4HciFromController(std::move(h4_2nd_fragment));
1387 
1388   // 4. Non-fragmented PDU to fine_channel.
1389   H4PacketWithHci h4_packet_2{emboss::H4PacketType::ACL_DATA,
1390                               pw::span(fine_hci_arr)};
1391   proxy.HandleH4HciFromController(std::move(h4_packet_2));
1392 
1393   // 5. 3rd of 3 fragments to frag_channel.
1394   if (kSduLength % 2 == 1) {
1395     acl_frag->data_total_length().Write(kSduLength / 2 + 1);
1396   }
1397   H4PacketWithHci h4_3rd_fragment{
1398       emboss::H4PacketType::ACL_DATA,
1399       pw::span<uint8_t>(frag_hci_arr.data(),
1400                         emboss::AclDataFrame::MinSizeInBytes() +
1401                             kSduLength / 2 + (kSduLength % 2))};
1402   proxy.HandleH4HciFromController(std::move(h4_3rd_fragment));
1403 
1404   // 6. Non-fragmented PDU to fine_channel.
1405   H4PacketWithHci h4_packet_3{emboss::H4PacketType::ACL_DATA,
1406                               pw::span(fine_hci_arr)};
1407   proxy.HandleH4HciFromController(std::move(h4_packet_3));
1408 
1409   // 3 non-fragmented PDUs plus 1 recombined PDU
1410   EXPECT_EQ(packets_received, 3 + 1);
1411 }
1412 
1413 // ########## L2capCocQueueTest
1414 
1415 class L2capCocQueueTest : public ProxyHostTest {};
1416 
TEST_F(L2capCocQueueTest,ReadBufferResponseDrainsQueue)1417 TEST_F(L2capCocQueueTest, ReadBufferResponseDrainsQueue) {
1418   size_t sends_called = 0;
1419 
1420   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1421       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1422   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1423       [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
1424         ++sends_called;
1425       });
1426   ProxyHost proxy =
1427       ProxyHost(std::move(send_to_host_fn),
1428                 std::move(send_to_controller_fn),
1429                 /*le_acl_credits_to_reserve=*/L2capCoc::QueueCapacity(),
1430                 /*br_edr_acl_credits_to_reserve=*/0);
1431 
1432   L2capCoc channel =
1433       BuildCoc(proxy, CocParameters{.tx_credits = L2capCoc::QueueCapacity()});
1434 
1435   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1436   for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
1437     PW_TEST_EXPECT_OK(channel.Write(multibuf::MultiBuf{}).status);
1438   }
1439   EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_UNAVAILABLE);
1440   EXPECT_EQ(sends_called, 0u);
1441 
1442   PW_TEST_EXPECT_OK(
1443       SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
1444 
1445   EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
1446 }
1447 
TEST_F(L2capCocQueueTest,NocpEventDrainsQueue)1448 TEST_F(L2capCocQueueTest, NocpEventDrainsQueue) {
1449   size_t sends_called = 0;
1450 
1451   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1452       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1453   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1454       [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
1455         ++sends_called;
1456       });
1457   ProxyHost proxy =
1458       ProxyHost(std::move(send_to_host_fn),
1459                 std::move(send_to_controller_fn),
1460                 /*le_acl_credits_to_reserve=*/L2capCoc::QueueCapacity(),
1461                 /*br_edr_acl_credits_to_reserve=*/0);
1462   PW_TEST_EXPECT_OK(
1463       SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
1464 
1465   uint16_t handle = 123;
1466   L2capCoc channel =
1467       BuildCoc(proxy,
1468                CocParameters{.handle = handle,
1469                              .tx_credits = 2 * L2capCoc::QueueCapacity()});
1470 
1471   for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
1472     PW_TEST_EXPECT_OK(channel.Write(multibuf::MultiBuf{}).status);
1473   }
1474 
1475   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1476   for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
1477     PW_TEST_EXPECT_OK(channel.Write(multibuf::MultiBuf{}).status);
1478   }
1479   EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, PW_STATUS_UNAVAILABLE);
1480   EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
1481 
1482   PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1483       proxy,
1484       FlatMap<uint16_t, uint16_t, 1>({{{handle, L2capCoc::QueueCapacity()}}})));
1485 
1486   EXPECT_EQ(sends_called, 2 * L2capCoc::QueueCapacity());
1487 }
1488 
TEST_F(L2capCocQueueTest,RemovingLrdChannelDoesNotInvalidateRoundRobin)1489 TEST_F(L2capCocQueueTest, RemovingLrdChannelDoesNotInvalidateRoundRobin) {
1490   size_t sends_called = 0;
1491   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1492       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1493   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1494       [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
1495         ++sends_called;
1496       });
1497   ProxyHost proxy =
1498       ProxyHost(std::move(send_to_host_fn),
1499                 std::move(send_to_controller_fn),
1500                 /*le_acl_credits_to_reserve=*/L2capCoc::QueueCapacity(),
1501                 /*br_edr_acl_credits_to_reserve=*/0);
1502   PW_TEST_EXPECT_OK(
1503       SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
1504   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), L2capCoc::QueueCapacity());
1505 
1506   uint16_t handle = 123;
1507   std::array<uint16_t, 3> remote_cids = {1, 2, 3};
1508   L2capCoc chan_left = BuildCoc(
1509       proxy,
1510       CocParameters{
1511           .handle = handle, .remote_cid = remote_cids[0], .tx_credits = 1});
1512   std::optional<L2capCoc> chan_middle =
1513       BuildCoc(proxy,
1514                CocParameters{.handle = handle,
1515                              .remote_cid = remote_cids[1],
1516                              .tx_credits = L2capCoc::QueueCapacity() + 1});
1517   L2capCoc chan_right = BuildCoc(
1518       proxy,
1519       CocParameters{
1520           .handle = handle, .remote_cid = remote_cids[2], .tx_credits = 1});
1521 
1522   // We have 3 channels. Make it so LRD channel iterator is on the middle
1523   // channel, then release that channel and ensure the other two are still
1524   // reached in the round robin.
1525 
1526   // Queue a packet in middle channel.
1527   for (size_t i = 0; i < L2capCoc::QueueCapacity() + 1; ++i) {
1528     PW_TEST_EXPECT_OK(chan_middle->Write(multibuf::MultiBuf{}).status);
1529   }
1530   EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
1531 
1532   // Make middle channel the LRD channel.
1533   PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1534       proxy, FlatMap<uint16_t, uint16_t, 1>({{{handle, 1}}})));
1535   EXPECT_EQ(sends_called, L2capCoc::QueueCapacity() + 1);
1536 
1537   // Queue a packet each in left and right channels.
1538   PW_TEST_EXPECT_OK(chan_left.Write(multibuf::MultiBuf{}).status);
1539   PW_TEST_EXPECT_OK(chan_right.Write(multibuf::MultiBuf{}).status);
1540   EXPECT_EQ(sends_called, L2capCoc::QueueCapacity() + 1);
1541 
1542   // Drop middle channel. LRD write iterator should still be valid.
1543   chan_middle.reset();
1544 
1545   // Confirm packets in remaining two channels are sent in round robin.
1546   PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1547       proxy, FlatMap<uint16_t, uint16_t, 1>({{{handle, 2}}})));
1548   EXPECT_EQ(sends_called, L2capCoc::QueueCapacity() + 3);
1549 }
1550 
TEST_F(L2capCocQueueTest,H4BufferReleaseTriggersQueueDrain)1551 TEST_F(L2capCocQueueTest, H4BufferReleaseTriggersQueueDrain) {
1552   constexpr size_t kNumSends =
1553       ProxyHost::GetNumSimultaneousAclSendsSupported() + 1;
1554 
1555   struct {
1556     size_t sends_called = 0;
1557     Vector<H4PacketWithH4, kNumSends> packet_store;
1558   } capture;
1559   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1560       []([[maybe_unused]] H4PacketWithHci&& packet) {});
1561   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1562       [&capture](H4PacketWithH4&& packet) {
1563         ++capture.sends_called;
1564         capture.packet_store.push_back(std::move(packet));
1565       });
1566   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1567                               std::move(send_to_controller_fn),
1568                               /*le_acl_credits_to_reserve=*/kNumSends,
1569                               /*br_edr_acl_credits_to_reserve=*/0);
1570   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, kNumSends));
1571   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), kNumSends);
1572 
1573   constexpr uint16_t kHandle = 0x123;
1574   constexpr uint16_t kRemoteCid = 0x456;
1575   L2capCoc channel = BuildCoc(proxy,
1576                               CocParameters{.handle = kHandle,
1577                                             .remote_cid = kRemoteCid,
1578                                             .tx_credits = kNumSends});
1579 
1580   // Occupy all buffers. Final Write should queue and not send.
1581   for (size_t i = 0; i < kNumSends; ++i) {
1582     PW_TEST_EXPECT_OK(channel.Write(multibuf::MultiBuf{}).status);
1583   }
1584   EXPECT_EQ(capture.sends_called, kNumSends - 1);
1585 
1586   // Release a buffer. Queued packet should then send.
1587   capture.packet_store.pop_back();
1588   EXPECT_EQ(capture.sends_called, kNumSends);
1589 
1590   capture.packet_store.clear();
1591 }
1592 
TEST_F(L2capCocQueueTest,RoundRobinHandlesMultiplePasses)1593 TEST_F(L2capCocQueueTest, RoundRobinHandlesMultiplePasses) {
1594   constexpr size_t kNumSends = L2capCoc::QueueCapacity();
1595   struct {
1596     size_t sends_called = 0;
1597     Vector<H4PacketWithH4, kNumSends> packet_store;
1598   } capture;
1599   pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1600       [](H4PacketWithHci&&) {});
1601   pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1602       [&capture](H4PacketWithH4&& packet) {
1603         ++capture.sends_called;
1604         // We prevent packets from being released in this test because each
1605         // packet release triggers a round robin.
1606         capture.packet_store.push_back(std::move(packet));
1607       });
1608   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1609                               std::move(send_to_controller_fn),
1610                               /*le_acl_credits_to_reserve=*/kNumSends,
1611                               /*br_edr_acl_credits_to_reserve=*/0);
1612 
1613   L2capCoc channel = BuildCoc(proxy, CocParameters{.tx_credits = kNumSends});
1614 
1615   // Occupy all queue slots.
1616   for (size_t i = 0; i < kNumSends; ++i) {
1617     PW_TEST_EXPECT_OK(channel.Write(multibuf::MultiBuf{}).status);
1618   }
1619   EXPECT_EQ(capture.sends_called, 0ul);
1620 
1621   // This will provide enough credits for all queued packets. So they all should
1622   // be drained and sent.
1623   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, kNumSends));
1624   EXPECT_EQ(capture.sends_called, kNumSends);
1625   capture.packet_store.clear();
1626 }
1627 
1628 // ########## L2capCocReassemblyTest
1629 
1630 class L2capCocReassemblyTest : public ProxyHostTest {};
1631 
TEST_F(L2capCocReassemblyTest,OneSegmentRx)1632 TEST_F(L2capCocReassemblyTest, OneSegmentRx) {
1633   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1634       [](H4PacketWithH4&&) {});
1635   pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1636       [](H4PacketWithHci&&) {});
1637   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1638                               std::move(send_to_controller_fn),
1639                               /*le_acl_credits_to_reserve=*/0,
1640                               /*br_edr_acl_credits_to_reserve=*/0);
1641 
1642   uint16_t handle = 0x123;
1643   uint16_t local_cid = 0x234;
1644   struct {
1645     int sdus_received = 0;
1646     std::array<uint8_t, 10> expected_payload = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
1647   } capture;
1648   L2capCoc channel = BuildCoc(
1649       proxy,
1650       {.handle = handle,
1651        .local_cid = local_cid,
1652        .receive_fn = [&capture](multibuf::MultiBuf&& payload) {
1653          ++capture.sdus_received;
1654          std::optional<ConstByteSpan> rx_sdu = payload.ContiguousSpan();
1655          ASSERT_TRUE(rx_sdu);
1656          ConstByteSpan expected_sdu = as_bytes(span(
1657              capture.expected_payload.data(), capture.expected_payload.size()));
1658          EXPECT_TRUE(std::equal(rx_sdu->begin(),
1659                                 rx_sdu->end(),
1660                                 expected_sdu.begin(),
1661                                 expected_sdu.end()));
1662        }});
1663 
1664   Result<BFrameWithStorage> bframe = SetupBFrame(
1665       handle, local_cid, capture.expected_payload.size() + kSduLengthFieldSize);
1666   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA,
1667                             bframe->acl.hci_span()};
1668 
1669   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
1670       bframe->acl.writer.payload().BackingStorage().data(),
1671       bframe->acl.writer.payload().SizeInBytes());
1672   kframe.sdu_length().Write(capture.expected_payload.size());
1673   PW_CHECK(TryToCopyToEmbossStruct(/*emboss_dest=*/kframe.payload(),
1674                                    /*src=*/capture.expected_payload));
1675 
1676   proxy.HandleH4HciFromController(std::move(h4_packet));
1677 
1678   EXPECT_EQ(capture.sdus_received, 1);
1679 }
1680 
TEST_F(L2capCocReassemblyTest,SduReceivedWhenSegmentedOverFullRangeOfMps)1681 TEST_F(L2capCocReassemblyTest, SduReceivedWhenSegmentedOverFullRangeOfMps) {
1682   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1683       [](H4PacketWithH4&&) {});
1684   pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1685       [](H4PacketWithHci&&) {});
1686   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1687                               std::move(send_to_controller_fn),
1688                               /*le_acl_credits_to_reserve=*/0,
1689                               /*br_edr_acl_credits_to_reserve=*/0);
1690 
1691   uint16_t handle = 0x123;
1692   uint16_t local_cid = 0x234;
1693   struct {
1694     uint16_t sdus_received = 0;
1695     std::array<uint8_t, 19> expected_payload = {
1696         1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
1697   } capture;
1698   L2capCoc channel = BuildCoc(
1699       proxy,
1700       {.handle = handle,
1701        .local_cid = local_cid,
1702        .receive_fn = [&capture](multibuf::MultiBuf&& payload) {
1703          ++capture.sdus_received;
1704          std::optional<ConstByteSpan> rx_sdu = payload.ContiguousSpan();
1705          ASSERT_TRUE(rx_sdu);
1706          ConstByteSpan expected_sdu = as_bytes(span(
1707              capture.expected_payload.data(), capture.expected_payload.size()));
1708          EXPECT_TRUE(std::equal(rx_sdu->begin(),
1709                                 rx_sdu->end(),
1710                                 expected_sdu.begin(),
1711                                 expected_sdu.end()));
1712        }});
1713 
1714   uint16_t sdus_sent = 0;
1715   // Test sending payload segmented in every possible way, from MPS of 2 octets
1716   // to MPS values 5 octets greater than the payload size.
1717   for (uint16_t mps = 2; mps < capture.expected_payload.size() + 5; ++mps) {
1718     for (size_t segment_no = 0;; ++segment_no) {
1719       Result<KFrameWithStorage> kframe = SetupKFrame(
1720           handle, local_cid, mps, segment_no, span(capture.expected_payload));
1721       if (!kframe.ok()) {
1722         break;
1723       }
1724       H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA,
1725                                 kframe->acl.hci_span()};
1726       proxy.HandleH4HciFromController(std::move(h4_packet));
1727     }
1728     ++sdus_sent;
1729   }
1730 
1731   EXPECT_EQ(capture.sdus_received, sdus_sent);
1732 }
1733 
TEST_F(L2capCocReassemblyTest,ErrorIfPayloadBytesExceedSduLength)1734 TEST_F(L2capCocReassemblyTest, ErrorIfPayloadBytesExceedSduLength) {
1735   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1736       [](H4PacketWithH4&&) {});
1737   pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1738       [](H4PacketWithHci&&) {});
1739   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1740                               std::move(send_to_controller_fn),
1741                               /*le_acl_credits_to_reserve=*/0,
1742                               /*br_edr_acl_credits_to_reserve=*/0);
1743 
1744   uint16_t handle = 0x123;
1745   uint16_t local_cid = 0x234;
1746   int events_received = 0;
1747   L2capCoc channel =
1748       BuildCoc(proxy,
1749                {
1750                    .handle = handle,
1751                    .local_cid = local_cid,
1752                    .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); },
1753                    .event_fn =
1754                        [&events_received](L2capChannelEvent event) {
1755                          ++events_received;
1756                          EXPECT_EQ(event, L2capChannelEvent::kRxInvalid);
1757                        },
1758                });
1759 
1760   constexpr uint16_t kIndicatedSduLength = 5;
1761   // First PDU will be 2 bytes for SDU length field + 2 payload bytes
1762   // Second PDU will have 4 payload bytes, which will exceed SDU length by 1.
1763   constexpr uint16_t kFirstPayloadLength = 2;
1764 
1765   std::array<uint8_t, kFirstKFrameOverAclMinSize + kFirstPayloadLength> hci_arr;
1766   hci_arr.fill(0);
1767   H4PacketWithHci first_h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1768 
1769   Result<emboss::AclDataFrameWriter> acl =
1770       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
1771   acl->header().handle().Write(handle);
1772   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
1773                                  kFirstPayloadLength);
1774 
1775   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
1776       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
1777   kframe.pdu_length().Write(kSduLengthFieldSize + kFirstPayloadLength);
1778   kframe.channel_id().Write(local_cid);
1779   kframe.sdu_length().Write(kIndicatedSduLength);
1780 
1781   proxy.HandleH4HciFromController(std::move(first_h4_packet));
1782 
1783   H4PacketWithHci second_h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1784   proxy.HandleH4HciFromController(std::move(second_h4_packet));
1785 
1786   EXPECT_EQ(events_received, 1);
1787 }
1788 
TEST_F(L2capCocReassemblyTest,ErrorIfRxBufferTooSmallForFirstKFrame)1789 TEST_F(L2capCocReassemblyTest, ErrorIfRxBufferTooSmallForFirstKFrame) {
1790   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1791       [](H4PacketWithH4&&) {});
1792   pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1793       [](H4PacketWithHci&&) {});
1794   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1795                               std::move(send_to_controller_fn),
1796                               /*le_acl_credits_to_reserve=*/0,
1797                               /*br_edr_acl_credits_to_reserve=*/0);
1798 
1799   uint16_t handle = 0x123;
1800   uint16_t local_cid = 0x234;
1801   int events_received = 0;
1802   L2capCoc channel =
1803       BuildCoc(proxy,
1804                {
1805                    .handle = handle,
1806                    .local_cid = local_cid,
1807                    .receive_fn = [](multibuf::MultiBuf&&) { FAIL(); },
1808                    .event_fn =
1809                        [&events_received](L2capChannelEvent event) {
1810                          ++events_received;
1811                          EXPECT_EQ(event, L2capChannelEvent::kRxInvalid);
1812                        },
1813                });
1814 
1815   std::array<uint8_t, kFirstKFrameOverAclMinSize - 1> hci_arr;
1816   hci_arr.fill(0);
1817   H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
1818 
1819   Result<emboss::AclDataFrameWriter> acl =
1820       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
1821   acl->header().handle().Write(handle);
1822   acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() - 1);
1823 
1824   emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
1825       acl->payload().BackingStorage().data(), acl->data_total_length().Read());
1826   EXPECT_TRUE(!kframe.IsComplete());
1827   kframe.pdu_length().Write(1);
1828   kframe.channel_id().Write(local_cid);
1829 
1830   proxy.HandleH4HciFromController(std::move(h4_packet));
1831 
1832   EXPECT_EQ(events_received, 1);
1833 }
1834 
1835 // ########## L2capCocSegmentation
1836 
1837 class L2capCocSegmentationTest : public ProxyHostTest {};
1838 
TEST_F(L2capCocSegmentationTest,SduSentWhenSegmentedOverFullRangeOfMps)1839 TEST_F(L2capCocSegmentationTest, SduSentWhenSegmentedOverFullRangeOfMps) {
1840   constexpr size_t kPayloadSize = 312;
1841   struct {
1842     uint16_t handle = 0x123;
1843     uint16_t remote_cid = 0x456;
1844     uint16_t sdus_received = 0;
1845     uint16_t mps;
1846     std::array<uint8_t, kPayloadSize> expected_payload;
1847   } capture;
1848 
1849   for (size_t i = 0; i < capture.expected_payload.size(); ++i) {
1850     capture.expected_payload[i] = i % UINT8_MAX;
1851   }
1852 
1853   pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1854       [&capture](H4PacketWithH4&& tx_kframe) {
1855         static uint16_t segment_no = 0;
1856         static uint16_t pdu_bytes_received = 0;
1857         PW_TEST_ASSERT_OK_AND_ASSIGN(
1858             KFrameWithStorage expected_kframe,
1859             SetupKFrame(capture.handle,
1860                         capture.remote_cid,
1861                         capture.mps,
1862                         segment_no,
1863                         span(capture.expected_payload)));
1864 
1865         EXPECT_TRUE(std::equal(tx_kframe.GetHciSpan().begin(),
1866                                tx_kframe.GetHciSpan().end(),
1867                                expected_kframe.acl.hci_span().begin(),
1868                                expected_kframe.acl.hci_span().end()));
1869 
1870         pdu_bytes_received +=
1871             expected_kframe.acl.writer.data_total_length().Read() -
1872             emboss::BasicL2capHeader::IntrinsicSizeInBytes();
1873 
1874         if (pdu_bytes_received ==
1875             capture.expected_payload.size() + kSduLengthFieldSize) {
1876           ++capture.sdus_received;
1877           segment_no = 0;
1878           pdu_bytes_received = 0;
1879         } else {
1880           ++segment_no;
1881         }
1882       });
1883   pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1884       [](H4PacketWithHci&&) {});
1885   ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1886                               std::move(send_to_controller_fn),
1887 
1888                               /*le_acl_credits_to_reserve=*/UINT8_MAX,
1889                               /*br_edr_acl_credits_to_reserve=*/0);
1890   PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
1891       proxy,
1892       /*num_credits_to_reserve=*/UINT8_MAX,
1893       /*le_acl_data_packet_length=*/UINT16_MAX));
1894 
1895   uint16_t sdus_sent = 0;
1896 
1897   // Test sending payload segmented in every possible way, from MPS of 23 octets
1898   // to MPS values 5 octets greater than the payload size. 23 bytes is the
1899   // minimum MPS supported for L2CAP channels.
1900   for (capture.mps = 23; capture.mps < kPayloadSize + 5; ++capture.mps) {
1901     L2capCoc channel = BuildCoc(proxy,
1902                                 {.handle = capture.handle,
1903                                  .remote_cid = capture.remote_cid,
1904                                  .tx_mtu = capture.expected_payload.size(),
1905                                  .tx_mps = capture.mps,
1906                                  .tx_credits = UINT8_MAX});
1907     PW_TEST_EXPECT_OK(
1908         channel.Write(MultiBufFromSpan(span(capture.expected_payload))).status);
1909     ++sdus_sent;
1910 
1911     // Replenish proxy's LE ACL send credits, or else only UINT8_MAX PDUs could
1912     // be sent in this test.
1913     PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1914         proxy,
1915         FlatMap<uint16_t, uint16_t, 1>(
1916             {{{capture.handle,
1917                static_cast<uint8_t>(UINT8_MAX -
1918                                     proxy.GetNumFreeLeAclPackets())}}})));
1919   }
1920 
1921   EXPECT_EQ(capture.sdus_received, sdus_sent);
1922 }
1923 
1924 }  // namespace
1925 }  // namespace pw::bluetooth::proxy
1926