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