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 "pw_bluetooth_proxy/proxy_host.h"
16
17 #include <cstdint>
18
19 #include "pw_bluetooth/emboss_util.h"
20 #include "pw_bluetooth/hci_commands.emb.h"
21 #include "pw_bluetooth/hci_common.emb.h"
22 #include "pw_bluetooth/hci_data.emb.h"
23 #include "pw_bluetooth/hci_events.emb.h"
24 #include "pw_bluetooth/hci_h4.emb.h"
25 #include "pw_bluetooth/l2cap_frames.emb.h"
26 #include "pw_bluetooth_proxy/h4_packet.h"
27 #include "pw_bluetooth_proxy/internal/logical_transport.h"
28 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
29 #include "pw_bluetooth_proxy/l2cap_status_delegate.h"
30 #include "pw_bluetooth_proxy_private/test_utils.h"
31 #include "pw_containers/flat_map.h"
32 #include "pw_function/function.h"
33 #include "pw_log/log.h"
34 #include "pw_span/span.h"
35 #include "pw_status/status.h"
36 #include "pw_unit_test/framework.h"
37 #include "pw_unit_test/status_macros.h"
38
39 namespace pw::bluetooth::proxy {
40
41 namespace {
42
43 using containers::FlatMap;
44
45 // Return a populated H4 command buffer of a type that proxy host doesn't
46 // interact with.
PopulateNoninteractingToControllerBuffer(H4PacketWithH4 & h4_packet)47 Status PopulateNoninteractingToControllerBuffer(H4PacketWithH4& h4_packet) {
48 return CreateAndPopulateToControllerView<emboss::InquiryCommandWriter>(
49 h4_packet,
50 emboss::OpCode::LINK_KEY_REQUEST_REPLY,
51 /*parameter_total_size=*/0)
52 .status();
53 }
54
55 // Return a populated H4 event buffer of a type that proxy host doesn't interact
56 // with.
CreateNonInteractingToHostBuffer(H4PacketWithHci & h4_packet)57 Status CreateNonInteractingToHostBuffer(H4PacketWithHci& h4_packet) {
58 return CreateAndPopulateToHostEventWriter<emboss::InquiryCompleteEventWriter>(
59 h4_packet, emboss::EventCode::INQUIRY_COMPLETE)
60 .status();
61 }
62
63 // ########## Examples
64
65 // Example for docs.rst.
TEST(Example,ExampleUsage)66 TEST(Example, ExampleUsage) {
67 // Populate H4 buffer to send towards controller.
68 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1>
69 h4_array_from_host{};
70 H4PacketWithH4 h4_packet_from_host{emboss::H4PacketType::UNKNOWN,
71 h4_array_from_host};
72 PW_TEST_EXPECT_OK(
73 PopulateNoninteractingToControllerBuffer(h4_packet_from_host));
74
75 // Populate H4 buffer to send towards host.
76 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes() + 1>
77 hci_array_from_controller{};
78 H4PacketWithHci h4_packet_from_controller{emboss::H4PacketType::UNKNOWN,
79 hci_array_from_controller};
80
81 PW_TEST_EXPECT_OK(
82 CreateNonInteractingToHostBuffer(h4_packet_from_controller));
83
84 pw::Function<void(H4PacketWithHci && packet)> container_send_to_host_fn(
85 []([[maybe_unused]] H4PacketWithHci&& packet) {});
86
87 pw::Function<void(H4PacketWithH4 && packet)> container_send_to_controller_fn(
88 ([]([[maybe_unused]] H4PacketWithH4&& packet) {}));
89
90 // DOCSTAG: [pw_bluetooth_proxy-examples-basic]
91
92 #include "pw_bluetooth_proxy/proxy_host.h"
93
94 // Container creates ProxyHost .
95 ProxyHost proxy = ProxyHost(std::move(container_send_to_host_fn),
96 std::move(container_send_to_controller_fn),
97 /*le_acl_credits_to_reserve=*/2,
98 /*br_edr_acl_credits_to_reserve=*/0);
99
100 // Container passes H4 packets from host through proxy. Proxy will in turn
101 // call the container-provided `container_send_to_controller_fn` to pass them
102 // on to the controller. Some packets may be modified, added, or removed.
103 proxy.HandleH4HciFromHost(std::move(h4_packet_from_host));
104
105 // Container passes H4 packets from controller through proxy. Proxy will in
106 // turn call the container-provided `container_send_to_host_fn` to pass them
107 // on to the controller. Some packets may be modified, added, or removed.
108 proxy.HandleH4HciFromController(std::move(h4_packet_from_controller));
109
110 // DOCSTAG: [pw_bluetooth_proxy-examples-basic]
111 }
112
113 // ########## PassthroughTest
114
115 class PassthroughTest : public ProxyHostTest {};
116
117 // Verify buffer is properly passed (contents unaltered and zero-copy).
TEST_F(PassthroughTest,ToControllerPassesEqualBuffer)118 TEST_F(PassthroughTest, ToControllerPassesEqualBuffer) {
119 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr{};
120 H4PacketWithH4 h4_packet{emboss::H4PacketType::UNKNOWN, h4_arr};
121 PW_TEST_EXPECT_OK(PopulateNoninteractingToControllerBuffer(h4_packet));
122
123 // Struct for capturing because `pw::Function` can't fit multiple captures.
124 struct {
125 // Use a copy for comparison to catch if proxy incorrectly changes the
126 // passed buffer.
127 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr;
128 H4PacketWithH4* h4_packet;
129 uint8_t sends_called;
130 } send_capture = {h4_arr, &h4_packet, 0};
131
132 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
133 [&send_capture](H4PacketWithH4&& packet) {
134 send_capture.sends_called++;
135 EXPECT_EQ(packet.GetH4Type(),
136 emboss::H4PacketType(send_capture.h4_arr[0]));
137 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
138 send_capture.h4_packet->GetHciSpan().end(),
139 send_capture.h4_arr.begin() + 1,
140 send_capture.h4_arr.end()));
141 // Verify no copy by verifying buffer is at the same memory location.
142 EXPECT_EQ(packet.GetHciSpan().data(),
143 send_capture.h4_packet->GetHciSpan().data());
144 });
145
146 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
147 []([[maybe_unused]] H4PacketWithHci&& packet) {});
148
149 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
150 std::move(send_to_controller_fn),
151 /*le_acl_credits_to_reserve=*/2,
152 /*br_edr_acl_credits_to_reserve=*/0);
153
154 proxy.HandleH4HciFromHost(std::move(h4_packet));
155
156 // Verify to controller callback was called.
157 EXPECT_EQ(send_capture.sends_called, 1);
158 }
159
160 // Verify buffer is properly passed (contents unaltered and zero-copy).
TEST_F(PassthroughTest,ToHostPassesEqualBuffer)161 TEST_F(PassthroughTest, ToHostPassesEqualBuffer) {
162 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
163 hci_arr{};
164 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
165 PW_TEST_EXPECT_OK(CreateNonInteractingToHostBuffer(h4_packet));
166
167 // Struct for capturing because `pw::Function` can't fit multiple captures.
168 struct {
169 // Use a copy for comparison to catch if proxy incorrectly changes the
170 // passed buffer.
171 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
172 hci_arr;
173 H4PacketWithHci* h4_packet;
174 uint8_t sends_called;
175 } send_capture = {hci_arr, &h4_packet, 0};
176
177 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
178 [&send_capture](H4PacketWithHci&& packet) {
179 send_capture.sends_called++;
180 EXPECT_EQ(packet.GetH4Type(), send_capture.h4_packet->GetH4Type());
181 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
182 send_capture.h4_packet->GetHciSpan().end(),
183 send_capture.h4_packet->GetHciSpan().begin(),
184 send_capture.h4_packet->GetHciSpan().end()));
185 // Verify no copy by verifying buffer is at the same memory location.
186 EXPECT_EQ(packet.GetHciSpan().data(),
187 send_capture.h4_packet->GetHciSpan().data());
188 });
189
190 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
191 []([[maybe_unused]] H4PacketWithH4&& packet) {});
192
193 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
194 std::move(send_to_controller_fn),
195 /*le_acl_credits_to_reserve=*/2,
196 /*br_edr_acl_credits_to_reserve=*/0);
197
198 proxy.HandleH4HciFromController(std::move(h4_packet));
199
200 // Verify to controller callback was called.
201 EXPECT_EQ(send_capture.sends_called, 1);
202 }
203
204 // Verify a command complete event (of a type that proxy doesn't act on) is
205 // properly passed (contents unaltered and zero-copy).
TEST_F(PassthroughTest,ToHostPassesEqualCommandComplete)206 TEST_F(PassthroughTest, ToHostPassesEqualCommandComplete) {
207 std::array<
208 uint8_t,
209 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
210 hci_arr{};
211 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
212 PW_TEST_ASSERT_OK_AND_ASSIGN(
213 auto view,
214 CreateAndPopulateToHostEventWriter<
215 emboss::ReadLocalVersionInfoCommandCompleteEventWriter>(
216 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
217 view.command_complete().command_opcode().Write(
218 emboss::OpCode::READ_LOCAL_VERSION_INFO);
219
220 // Struct for capturing because `pw::Function` can't fit multiple captures.
221 struct {
222 std::array<
223 uint8_t,
224 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
225 hci_arr;
226 H4PacketWithHci* h4_packet;
227 uint8_t sends_called;
228 } send_capture = {hci_arr, &h4_packet, 0};
229
230 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
231 [&send_capture](H4PacketWithHci&& packet) {
232 send_capture.sends_called++;
233 EXPECT_EQ(packet.GetH4Type(), send_capture.h4_packet->GetH4Type());
234 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
235 send_capture.h4_packet->GetHciSpan().end(),
236 send_capture.h4_packet->GetHciSpan().begin(),
237 send_capture.h4_packet->GetHciSpan().end()));
238 // Verify no copy by verifying buffer is at the same memory location.
239 EXPECT_EQ(packet.GetHciSpan().data(),
240 send_capture.h4_packet->GetHciSpan().data());
241 });
242
243 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
244 []([[maybe_unused]] H4PacketWithH4&& packet) {});
245
246 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
247 std::move(send_to_controller_fn),
248 /*le_acl_credits_to_reserve=*/2,
249 /*br_edr_acl_credits_to_reserve=*/0);
250
251 proxy.HandleH4HciFromController(std::move(h4_packet));
252
253 // Verify to controller callback was called.
254 EXPECT_EQ(send_capture.sends_called, 1);
255 }
256
257 // ########## BadPacketTest
258 // The proxy should not affect buffers it can't process (it should just pass
259 // them on).
260
261 class BadPacketTest : public ProxyHostTest {};
262
TEST_F(BadPacketTest,BadH4TypeToControllerIsPassedOn)263 TEST_F(BadPacketTest, BadH4TypeToControllerIsPassedOn) {
264 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr{};
265 H4PacketWithH4 h4_packet{emboss::H4PacketType::UNKNOWN, h4_arr};
266 PW_TEST_EXPECT_OK(PopulateNoninteractingToControllerBuffer(h4_packet));
267 // Set back to an invalid type (after
268 // PopulateNoninteractingToControllerBuffer).
269 h4_packet.SetH4Type(emboss::H4PacketType::UNKNOWN);
270
271 // Struct for capturing because `pw::Function` can't fit multiple captures.
272 struct {
273 // Use a copy for comparison to catch if proxy incorrectly changes the
274 // passed buffer.
275 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr;
276 H4PacketWithH4* h4_packet;
277 uint8_t sends_called;
278 } send_capture = {h4_arr, &h4_packet, 0};
279
280 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
281 [&send_capture](H4PacketWithH4&& packet) {
282 send_capture.sends_called++;
283 EXPECT_EQ(packet.GetH4Type(),
284 emboss::H4PacketType(send_capture.h4_arr[0]));
285 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
286 send_capture.h4_packet->GetHciSpan().end(),
287 send_capture.h4_arr.begin() + 1,
288 send_capture.h4_arr.end()));
289 // Verify no copy by verifying buffer is at the same memory location.
290 EXPECT_EQ(packet.GetHciSpan().data(),
291 send_capture.h4_packet->GetHciSpan().data());
292 });
293
294 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
295 []([[maybe_unused]] H4PacketWithHci&& packet) {});
296
297 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
298 std::move(send_to_controller_fn),
299 /*le_acl_credits_to_reserve=*/2,
300 /*br_edr_acl_credits_to_reserve=*/0);
301
302 proxy.HandleH4HciFromHost(std::move(h4_packet));
303
304 // Verify to controller callback was called.
305 EXPECT_EQ(send_capture.sends_called, 1);
306 }
307
TEST_F(BadPacketTest,BadH4TypeToHostIsPassedOn)308 TEST_F(BadPacketTest, BadH4TypeToHostIsPassedOn) {
309 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
310 hci_arr{};
311 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
312 PW_TEST_EXPECT_OK(CreateNonInteractingToHostBuffer(h4_packet));
313
314 // Set back to an invalid type.
315 h4_packet.SetH4Type(emboss::H4PacketType::UNKNOWN);
316
317 // Struct for capturing because `pw::Function` can't fit multiple captures.
318 struct {
319 // Use a copy for comparison to catch if proxy incorrectly changes the
320 // passed buffer.
321 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
322 hci_arr;
323 H4PacketWithHci* h4_packet;
324 uint8_t sends_called = 0;
325 } send_capture = {hci_arr, &h4_packet, 0};
326
327 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
328 [&send_capture](H4PacketWithHci&& packet) {
329 send_capture.sends_called++;
330 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::UNKNOWN);
331 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
332 send_capture.h4_packet->GetHciSpan().end(),
333 send_capture.h4_packet->GetHciSpan().begin(),
334 send_capture.h4_packet->GetHciSpan().end()));
335 // Verify no copy by verifying buffer is at the same memory location.
336 EXPECT_EQ(packet.GetHciSpan().data(),
337 send_capture.h4_packet->GetHciSpan().data());
338 });
339
340 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
341 []([[maybe_unused]] H4PacketWithH4&& packet) {});
342
343 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
344 std::move(send_to_controller_fn),
345 /*le_acl_credits_to_reserve=*/2,
346 /*br_edr_acl_credits_to_reserve=*/0);
347
348 proxy.HandleH4HciFromController(std::move(h4_packet));
349
350 // Verify to controller callback was called.
351 EXPECT_EQ(send_capture.sends_called, 1);
352 }
353
TEST_F(BadPacketTest,EmptyBufferToControllerIsPassedOn)354 TEST_F(BadPacketTest, EmptyBufferToControllerIsPassedOn) {
355 std::array<uint8_t, 0> h4_arr;
356 H4PacketWithH4 h4_packet{emboss::H4PacketType::COMMAND, h4_arr};
357 // H4PacketWithH4 use the underlying h4 buffer to store type. Since its length
358 // is zero, it can't store it and will always return UNKNOWN.
359 EXPECT_EQ(h4_packet.GetH4Type(), emboss::H4PacketType::UNKNOWN);
360
361 uint8_t sends_called = 0;
362 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
363 [&sends_called](H4PacketWithH4&& packet) {
364 sends_called++;
365 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::UNKNOWN);
366 EXPECT_TRUE(packet.GetHciSpan().empty());
367 });
368
369 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
370 []([[maybe_unused]] H4PacketWithHci&& packet) {});
371
372 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
373 std::move(send_to_controller_fn),
374 /*le_acl_credits_to_reserve=*/2,
375 /*br_edr_acl_credits_to_reserve=*/0);
376
377 proxy.HandleH4HciFromHost(std::move(h4_packet));
378
379 // Verify callback was called.
380 EXPECT_EQ(sends_called, 1);
381 }
382
TEST_F(BadPacketTest,EmptyBufferToHostIsPassedOn)383 TEST_F(BadPacketTest, EmptyBufferToHostIsPassedOn) {
384 std::array<uint8_t, 0> hci_arr;
385 H4PacketWithHci h4_packet{emboss::H4PacketType::EVENT, hci_arr};
386
387 uint8_t sends_called = 0;
388 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
389 [&sends_called](H4PacketWithHci&& packet) {
390 sends_called++;
391 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::EVENT);
392 EXPECT_TRUE(packet.GetHciSpan().empty());
393 });
394
395 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
396 []([[maybe_unused]] H4PacketWithH4&& packet) {});
397
398 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
399 std::move(send_to_controller_fn),
400 /*le_acl_credits_to_reserve=*/2,
401 /*br_edr_acl_credits_to_reserve=*/0);
402
403 proxy.HandleH4HciFromController(std::move(h4_packet));
404
405 // Verify callback was called.
406 EXPECT_EQ(sends_called, 1);
407 }
408
TEST_F(BadPacketTest,TooShortEventToHostIsPassOn)409 TEST_F(BadPacketTest, TooShortEventToHostIsPassOn) {
410 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
411 valid_hci_arr{};
412 H4PacketWithHci valid_packet{emboss::H4PacketType::UNKNOWN, valid_hci_arr};
413 PW_TEST_EXPECT_OK(CreateNonInteractingToHostBuffer(valid_packet));
414
415 // Create packet for sending whose span size is one less than a valid command
416 // complete event.
417 H4PacketWithHci h4_packet{valid_packet.GetH4Type(),
418 valid_packet.GetHciSpan().subspan(
419 0, emboss::EventHeaderView::SizeInBytes() - 1)};
420
421 // Struct for capturing because `pw::Function` can't fit multiple captures.
422 struct {
423 std::array<uint8_t, emboss::EventHeaderView::SizeInBytes() - 1> hci_arr;
424 uint8_t sends_called = 0;
425 } send_capture;
426 // Copy valid event into a short_array whose size is one less than a valid
427 // EventHeader.
428 std::copy(h4_packet.GetHciSpan().begin(),
429 h4_packet.GetHciSpan().end(),
430 send_capture.hci_arr.begin());
431 send_capture.sends_called = 0;
432
433 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
434 [&send_capture](H4PacketWithHci&& packet) {
435 send_capture.sends_called++;
436 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
437 packet.GetHciSpan().end(),
438 send_capture.hci_arr.begin(),
439 send_capture.hci_arr.end()));
440 });
441
442 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
443 []([[maybe_unused]] H4PacketWithH4&& packet) {});
444
445 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
446 std::move(send_to_controller_fn),
447 /*le_acl_credits_to_reserve=*/2,
448 /*br_edr_acl_credits_to_reserve=*/0);
449
450 proxy.HandleH4HciFromController(std::move(h4_packet));
451
452 // Verify callback was called.
453 EXPECT_EQ(send_capture.sends_called, 1);
454 }
455
TEST_F(BadPacketTest,TooShortCommandCompleteEventToHost)456 TEST_F(BadPacketTest, TooShortCommandCompleteEventToHost) {
457 std::array<
458 uint8_t,
459 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
460 valid_hci_arr{};
461 H4PacketWithHci valid_packet{emboss::H4PacketType::UNKNOWN, valid_hci_arr};
462 PW_TEST_ASSERT_OK_AND_ASSIGN(
463 auto view,
464 CreateAndPopulateToHostEventWriter<
465 emboss::ReadLocalVersionInfoCommandCompleteEventWriter>(
466 valid_packet, emboss::EventCode::COMMAND_COMPLETE));
467 view.command_complete().command_opcode().Write(
468 emboss::OpCode::READ_LOCAL_VERSION_INFO);
469
470 // Create packet for sending whose span size is one less than a valid command
471 // complete event.
472 H4PacketWithHci h4_packet{
473 valid_packet.GetH4Type(),
474 valid_packet.GetHciSpan().subspan(
475 0,
476 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::
477 SizeInBytes() -
478 1)};
479
480 // Struct for capturing because `pw::Function` capture can't fit multiple
481 // fields .
482 struct {
483 std::array<
484 uint8_t,
485 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes() -
486 1>
487 hci_arr;
488 uint8_t sends_called = 0;
489 } send_capture;
490 std::copy(h4_packet.GetHciSpan().begin(),
491 h4_packet.GetHciSpan().end(),
492 send_capture.hci_arr.begin());
493 send_capture.sends_called = 0;
494
495 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
496 [&send_capture](H4PacketWithHci&& packet) {
497 send_capture.sends_called++;
498 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
499 packet.GetHciSpan().end(),
500 send_capture.hci_arr.begin(),
501 send_capture.hci_arr.end()));
502 });
503
504 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
505 []([[maybe_unused]] H4PacketWithH4&& packet) {});
506
507 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
508 std::move(send_to_controller_fn),
509 /*le_acl_credits_to_reserve=*/2,
510 /*br_edr_acl_credits_to_reserve=*/0);
511
512 proxy.HandleH4HciFromController(std::move(h4_packet));
513
514 // Verify callback was called.
515 EXPECT_EQ(send_capture.sends_called, 1);
516 }
517
518 // ########## ReserveLeAclCreditsTest
519
520 class ReserveLeAclCreditsTest : public ProxyHostTest {};
521
522 // Proxy Host should reserve requested ACL credits from controller's ACL credits
523 // when using ReadBufferSize command.
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsReserveCreditsWithReadBufferSize)524 TEST_F(ReserveLeAclCreditsTest, ProxyCreditsReserveCreditsWithReadBufferSize) {
525 std::array<uint8_t,
526 emboss::ReadBufferSizeCommandCompleteEventWriter::SizeInBytes()>
527 hci_arr{};
528 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
529 PW_TEST_ASSERT_OK_AND_ASSIGN(
530 auto view,
531 CreateAndPopulateToHostEventWriter<
532 emboss::ReadBufferSizeCommandCompleteEventWriter>(
533 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
534 view.command_complete().command_opcode().Write(
535 emboss::OpCode::READ_BUFFER_SIZE);
536 view.total_num_acl_data_packets().Write(10);
537
538 uint8_t sends_called = 0;
539 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
540 [&sends_called](H4PacketWithHci&& received_packet) {
541 sends_called++;
542 PW_TEST_ASSERT_OK_AND_ASSIGN(
543 auto event_view,
544 MakeEmbossWriter<emboss::ReadBufferSizeCommandCompleteEventWriter>(
545 received_packet.GetHciSpan()));
546 // Should reserve 2 credits from original total of 10 (so 8 left for
547 // host).
548 EXPECT_EQ(event_view.total_num_acl_data_packets().Read(), 8);
549 });
550
551 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
552 []([[maybe_unused]] H4PacketWithH4&& packet) {});
553
554 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
555 std::move(send_to_controller_fn),
556 /*le_acl_credits_to_reserve=*/0,
557 /*br_edr_acl_credits_to_reserve=*/2);
558
559 proxy.HandleH4HciFromController(std::move(h4_packet));
560
561 EXPECT_EQ(proxy.GetNumFreeBrEdrAclPackets(), 2);
562
563 EXPECT_TRUE(proxy.HasSendBrEdrAclCapability());
564
565 // Verify to controller callback was called.
566 EXPECT_EQ(sends_called, 1);
567 }
568
569 // Proxy Host should reserve requested ACL LE credits from controller's ACL LE
570 // credits when using LEReadBufferSizeV1 command.
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsReserveCreditsWithLEReadBufferSizeV1)571 TEST_F(ReserveLeAclCreditsTest,
572 ProxyCreditsReserveCreditsWithLEReadBufferSizeV1) {
573 std::array<
574 uint8_t,
575 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
576 hci_arr{};
577 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
578 PW_TEST_ASSERT_OK_AND_ASSIGN(
579 auto view,
580 CreateAndPopulateToHostEventWriter<
581 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
582 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
583 view.command_complete().command_opcode().Write(
584 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
585 view.total_num_le_acl_data_packets().Write(10);
586
587 uint8_t sends_called = 0;
588 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
589 [&sends_called](H4PacketWithHci&& received_packet) {
590 sends_called++;
591 PW_TEST_ASSERT_OK_AND_ASSIGN(
592 auto event_view,
593 MakeEmbossView<
594 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
595 received_packet.GetHciSpan()));
596
597 // Should reserve 2 credits from original total of 10 (so 8 left for
598 // host).
599 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 8);
600 });
601
602 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
603 []([[maybe_unused]] H4PacketWithH4&& packet) {});
604
605 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
606 std::move(send_to_controller_fn),
607 /*le_acl_credits_to_reserve=*/2,
608 /*br_edr_acl_credits_to_reserve=*/0);
609
610 proxy.HandleH4HciFromController(std::move(h4_packet));
611
612 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
613
614 EXPECT_TRUE(proxy.HasSendLeAclCapability());
615
616 // Verify to controller callback was called.
617 EXPECT_EQ(sends_called, 1);
618 }
619
620 // Proxy Host should reserve requested ACL LE credits from controller's ACL LE
621 // credits when using LEReadBufferSizeV2 command.
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsReserveCreditsWithLEReadBufferSizeV2)622 TEST_F(ReserveLeAclCreditsTest,
623 ProxyCreditsReserveCreditsWithLEReadBufferSizeV2) {
624 std::array<
625 uint8_t,
626 emboss::LEReadBufferSizeV2CommandCompleteEventWriter::SizeInBytes()>
627 hci_arr{};
628 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
629 PW_TEST_ASSERT_OK_AND_ASSIGN(
630 auto view,
631 CreateAndPopulateToHostEventWriter<
632 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
633 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
634 view.command_complete().command_opcode().Write(
635 emboss::OpCode::LE_READ_BUFFER_SIZE_V2);
636 view.total_num_le_acl_data_packets().Write(10);
637
638 uint8_t sends_called = 0;
639 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
640 [&sends_called](H4PacketWithHci&& received_packet) {
641 sends_called++;
642 PW_TEST_ASSERT_OK_AND_ASSIGN(
643 auto event_view,
644 MakeEmbossView<
645 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
646 received_packet.GetHciSpan()));
647 // Should reserve 2 credits from original total of 10 (so 8 left for
648 // host).
649 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 8);
650 });
651
652 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
653 []([[maybe_unused]] H4PacketWithH4&& packet) {});
654
655 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
656 std::move(send_to_controller_fn),
657 /*le_acl_credits_to_reserve=*/2,
658 /*br_edr_acl_credits_to_reserve=*/0);
659
660 proxy.HandleH4HciFromController(std::move(h4_packet));
661
662 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
663
664 EXPECT_TRUE(proxy.HasSendLeAclCapability());
665
666 // Verify to controller callback was called.
667 EXPECT_EQ(sends_called, 1);
668 }
669
670 // If controller provides less than wanted credits, we should reserve that
671 // smaller amount.
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsCappedByControllerCredits)672 TEST_F(ReserveLeAclCreditsTest, ProxyCreditsCappedByControllerCredits) {
673 std::array<
674 uint8_t,
675 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
676 hci_arr{};
677 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
678 PW_TEST_ASSERT_OK_AND_ASSIGN(
679 auto view,
680 CreateAndPopulateToHostEventWriter<
681 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
682 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
683 view.command_complete().command_opcode().Write(
684 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
685 view.total_num_le_acl_data_packets().Write(5);
686
687 uint8_t sends_called = 0;
688 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
689 [&sends_called](H4PacketWithHci&& received_packet) {
690 sends_called++;
691 // We want 7, but can reserve only 5 from original 5 (so 0 left for
692 // host).
693 PW_TEST_ASSERT_OK_AND_ASSIGN(
694 auto event_view,
695 MakeEmbossView<
696 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
697 received_packet.GetHciSpan()));
698 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 0);
699 });
700
701 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
702 []([[maybe_unused]] H4PacketWithH4&& packet) {});
703
704 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
705 std::move(send_to_controller_fn),
706 /*le_acl_credits_to_reserve=*/7,
707 /*br_edr_acl_credits_to_reserve=*/0);
708
709 proxy.HandleH4HciFromController(std::move(h4_packet));
710
711 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 5);
712
713 // Verify to controller callback was called.
714 EXPECT_EQ(sends_called, 1);
715 }
716
717 // Proxy Host can reserve zero credits from controller's ACL LE credits.
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsReserveZeroCredits)718 TEST_F(ReserveLeAclCreditsTest, ProxyCreditsReserveZeroCredits) {
719 std::array<
720 uint8_t,
721 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
722 hci_arr{};
723 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
724 PW_TEST_ASSERT_OK_AND_ASSIGN(
725 auto view,
726 CreateAndPopulateToHostEventWriter<
727 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
728 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
729 view.command_complete().command_opcode().Write(
730 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
731 view.total_num_le_acl_data_packets().Write(10);
732
733 uint8_t sends_called = 0;
734 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
735 [&sends_called](H4PacketWithHci&& received_packet) {
736 sends_called++;
737 PW_TEST_ASSERT_OK_AND_ASSIGN(
738 auto event_view,
739 MakeEmbossView<
740 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
741 received_packet.GetHciSpan()));
742 // Should reserve 0 credits from original total of 10 (so 10 left for
743 // host).
744 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 10);
745 });
746
747 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
748 []([[maybe_unused]] H4PacketWithH4&& packet) {});
749
750 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
751 std::move(send_to_controller_fn),
752 /*le_acl_credits_to_reserve=*/0,
753 /*br_edr_acl_credits_to_reserve=*/0);
754
755 proxy.HandleH4HciFromController(std::move(h4_packet));
756
757 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
758
759 EXPECT_FALSE(proxy.HasSendLeAclCapability());
760
761 // Verify to controller callback was called.
762 EXPECT_EQ(sends_called, 1);
763 }
764
765 // If controller has no credits, proxy should reserve none.
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsZeroWhenHostCreditsZero)766 TEST_F(ReserveLeAclCreditsTest, ProxyCreditsZeroWhenHostCreditsZero) {
767 std::array<
768 uint8_t,
769 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
770 hci_arr{};
771 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
772 PW_TEST_ASSERT_OK_AND_ASSIGN(
773 auto view,
774 CreateAndPopulateToHostEventWriter<
775 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
776 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
777 view.command_complete().command_opcode().Write(
778 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
779 view.total_num_le_acl_data_packets().Write(0);
780
781 uint8_t sends_called = 0;
782 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
783 [&sends_called](H4PacketWithHci&& received_packet) {
784 sends_called++;
785 PW_TEST_ASSERT_OK_AND_ASSIGN(
786 auto event_view,
787 MakeEmbossView<
788 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
789 received_packet.GetHciSpan()));
790 // Should reserve 0 credit from original total of 0 (so 0 left for
791 // host).
792 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 0);
793 });
794
795 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
796 []([[maybe_unused]] H4PacketWithH4&& packet) {});
797
798 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
799 std::move(send_to_controller_fn),
800 /*le_acl_credits_to_reserve=*/2,
801 /*br_edr_acl_credits_to_reserve=*/0);
802
803 proxy.HandleH4HciFromController(std::move(h4_packet));
804
805 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
806
807 EXPECT_TRUE(proxy.HasSendLeAclCapability());
808
809 // Verify to controller callback was called.
810 EXPECT_EQ(sends_called, 1);
811 }
812
TEST_F(ReserveLeAclCreditsTest,ProxyCreditsZeroWhenNotInitialized)813 TEST_F(ReserveLeAclCreditsTest, ProxyCreditsZeroWhenNotInitialized) {
814 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
815 []([[maybe_unused]] H4PacketWithHci&& packet) {});
816
817 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
818 []([[maybe_unused]] H4PacketWithH4&& packet) {});
819
820 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
821 std::move(send_to_controller_fn),
822 /*le_acl_credits_to_reserve=*/2,
823 /*br_edr_acl_credits_to_reserve=*/0);
824
825 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
826
827 EXPECT_TRUE(proxy.HasSendLeAclCapability());
828 }
829
830 // ########## NumberOfCompletedPacketsTest
831
832 class NumberOfCompletedPacketsTest : public ProxyHostTest {};
833
TEST_F(NumberOfCompletedPacketsTest,TwoOfThreeSentPacketsComplete)834 TEST_F(NumberOfCompletedPacketsTest, TwoOfThreeSentPacketsComplete) {
835 constexpr size_t kNumConnections = 3;
836 struct {
837 int sends_called = 0;
838 const std::array<uint16_t, kNumConnections> connection_handles = {
839 0x123, 0x456, 0x789};
840 } capture;
841
842 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
843 [&capture](H4PacketWithHci&& packet) {
844 PW_TEST_ASSERT_OK_AND_ASSIGN(
845 auto event_header,
846 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
847 0, emboss::EventHeader::IntrinsicSizeInBytes())));
848 capture.sends_called++;
849 if (event_header.event_code().Read() !=
850 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
851 return;
852 }
853
854 PW_TEST_ASSERT_OK_AND_ASSIGN(
855 auto view,
856 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
857 packet.GetHciSpan()));
858 EXPECT_EQ(packet.GetHciSpan().size(), 15ul);
859 EXPECT_EQ(view.num_handles().Read(), capture.connection_handles.size());
860 EXPECT_EQ(view.header().event_code().Read(),
861 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
862
863 // Proxy should have reclaimed 1 credit from Connection 0 (leaving 0
864 // credits in packet), no credits from Connection 1 (meaning 0 will be
865 // unchanged), and 1 credit from Connection 2 (leaving 0).
866 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
867 capture.connection_handles[0]);
868 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 0);
869
870 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
871 capture.connection_handles[1]);
872 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 0);
873
874 EXPECT_EQ(view.nocp_data()[2].connection_handle().Read(),
875 capture.connection_handles[2]);
876 EXPECT_EQ(view.nocp_data()[2].num_completed_packets().Read(), 0);
877 });
878 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
879 []([[maybe_unused]] H4PacketWithH4&& packet) {});
880
881 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
882 std::move(send_to_controller_fn),
883 /*le_acl_credits_to_reserve=*/kNumConnections,
884 /*br_edr_acl_credits_to_reserve=*/0);
885 PW_TEST_EXPECT_OK(
886 SendLeReadBufferResponseFromController(proxy, kNumConnections));
887 EXPECT_EQ(capture.sends_called, 1);
888
889 std::array<uint8_t, 1> attribute_value = {0};
890
891 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 3);
892
893 // Send packet; num free packets should decrement.
894 {
895 GattNotifyChannel channel = BuildGattNotifyChannel(
896 proxy, {.handle = capture.connection_handles[0]});
897 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
898 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
899 // Proxy host took all credits so will not pass NOCP on to host.
900 EXPECT_EQ(capture.sends_called, 1);
901 }
902
903 // Send packet over Connection 1, which will not have a packet completed in
904 // the Number_of_Completed_Packets event.
905 {
906 GattNotifyChannel channel = BuildGattNotifyChannel(
907 proxy, {.handle = capture.connection_handles[1]});
908 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
909 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
910 }
911
912 // Send third packet; num free packets should decrement again.
913 {
914 GattNotifyChannel channel = BuildGattNotifyChannel(
915 proxy, {.handle = capture.connection_handles[2]});
916 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
917 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
918 }
919
920 // Send Number_of_Completed_Packets event that reports 1 packet on Connection
921 // 0, 0 packets on Connection 1, and 1 packet on Connection 2. Checks in
922 // send_to_host_fn will ensure we have reclaimed 2 of 3 credits.
923 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
924 proxy,
925 FlatMap<uint16_t, uint16_t, 3>({{{capture.connection_handles[0], 1},
926 {capture.connection_handles[1], 0},
927 {capture.connection_handles[2], 1}}})));
928 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
929 // Proxy host took all credits so will not pass NOCP event on to host.
930 EXPECT_EQ(capture.sends_called, 1);
931 }
932
TEST_F(NumberOfCompletedPacketsTest,ManyMorePacketsCompletedThanPacketsPending)933 TEST_F(NumberOfCompletedPacketsTest,
934 ManyMorePacketsCompletedThanPacketsPending) {
935 constexpr size_t kNumConnections = 2;
936 struct {
937 int sends_called = 0;
938 const std::array<uint16_t, kNumConnections> connection_handles = {0x123,
939 0x456};
940 } capture;
941
942 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
943 [&capture](H4PacketWithHci&& packet) {
944 PW_TEST_ASSERT_OK_AND_ASSIGN(
945 auto event_header,
946 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
947 0, emboss::EventHeader::IntrinsicSizeInBytes())));
948 capture.sends_called++;
949 if (event_header.event_code().Read() !=
950 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
951 return;
952 }
953
954 PW_TEST_ASSERT_OK_AND_ASSIGN(
955 auto view,
956 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
957 packet.GetHciSpan()));
958 EXPECT_EQ(packet.GetHciSpan().size(), 11ul);
959 EXPECT_EQ(view.num_handles().Read(), capture.connection_handles.size());
960 EXPECT_EQ(view.header().event_code().Read(),
961 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
962
963 // Proxy should have reclaimed 1 credit from Connection 0 (leaving
964 // 9 credits in packet) and 1 credit from Connection 2 (leaving 14).
965 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
966 capture.connection_handles[0]);
967 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 9);
968
969 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
970 capture.connection_handles[1]);
971 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 14);
972 });
973 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
974 []([[maybe_unused]] H4PacketWithH4&& packet) {});
975
976 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
977 std::move(send_to_controller_fn),
978 /*le_acl_credits_to_reserve=*/2,
979 /*br_edr_acl_credits_to_reserve=*/0);
980 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
981 EXPECT_EQ(capture.sends_called, 1);
982
983 std::array<uint8_t, 1> attribute_value = {0};
984
985 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
986
987 // Send packet over Connection 0; num free packets should decrement.
988 {
989 GattNotifyChannel channel = BuildGattNotifyChannel(
990 proxy, {.handle = capture.connection_handles[0]});
991 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
992 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
993 }
994
995 // Send packet over Connection 1; num free packets should decrement again.
996 {
997 GattNotifyChannel channel = BuildGattNotifyChannel(
998 proxy, {.handle = capture.connection_handles[1]});
999 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1000 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1001 }
1002
1003 // Send Number_of_Completed_Packets event that reports 10 packets on
1004 // Connection 0 and 15 packets on Connection 1. Checks in send_to_host_fn
1005 // will ensure we have reclaimed exactly 2 credits, 1 from each Connection.
1006 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1007 proxy,
1008 FlatMap<uint16_t, uint16_t, 2>({{{capture.connection_handles[0], 10},
1009 {capture.connection_handles[1], 15}}})));
1010 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1011 EXPECT_EQ(capture.sends_called, 2);
1012 }
1013
TEST_F(NumberOfCompletedPacketsTest,ProxyReclaimsOnlyItsUsedCredits)1014 TEST_F(NumberOfCompletedPacketsTest, ProxyReclaimsOnlyItsUsedCredits) {
1015 constexpr size_t kNumConnections = 2;
1016 struct {
1017 int sends_called = 0;
1018 const std::array<uint16_t, kNumConnections> connection_handles = {0x123,
1019 0x456};
1020 } capture;
1021
1022 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1023 [&capture](H4PacketWithHci&& packet) {
1024 PW_TEST_ASSERT_OK_AND_ASSIGN(
1025 auto event_header,
1026 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1027 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1028 capture.sends_called++;
1029 if (event_header.event_code().Read() !=
1030 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1031 return;
1032 }
1033
1034 PW_TEST_ASSERT_OK_AND_ASSIGN(
1035 auto view,
1036 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1037 packet.GetHciSpan()));
1038 EXPECT_EQ(packet.GetHciSpan().size(), 11ul);
1039 EXPECT_EQ(view.num_handles().Read(), 2);
1040 EXPECT_EQ(view.header().event_code().Read(),
1041 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1042
1043 // Proxy has 4 credits it wants to reclaim, but it should have only
1044 // reclaimed the 2 credits it used on Connection 0.
1045 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1046 capture.connection_handles[0]);
1047 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 8);
1048 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
1049 capture.connection_handles[1]);
1050 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 15);
1051 });
1052 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1053 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1054
1055 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1056 std::move(send_to_controller_fn),
1057 /*le_acl_credits_to_reserve=*/4,
1058 /*br_edr_acl_credits_to_reserve=*/0);
1059 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 4));
1060 EXPECT_EQ(capture.sends_called, 1);
1061
1062 std::array<uint8_t, 1> attribute_value = {0};
1063
1064 // Use 2 credits on Connection 0 and 2 credits on random connections that will
1065 // not be included in the NOCP event.
1066 {
1067 GattNotifyChannel channel = BuildGattNotifyChannel(
1068 proxy, {.handle = capture.connection_handles[0]});
1069 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1070 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1071 }
1072 {
1073 GattNotifyChannel channel =
1074 BuildGattNotifyChannel(proxy, {.handle = 0xABC});
1075 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1076 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1077 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1078 }
1079
1080 // Send Number_of_Completed_Packets event that reports 10 packets on
1081 // Connection 0 and 15 packets on Connection 1. Checks in send_to_host_fn
1082 // will ensure we have reclaimed only 2 credits.
1083 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1084 proxy,
1085 FlatMap<uint16_t, uint16_t, 2>({{{capture.connection_handles[0], 10},
1086 {capture.connection_handles[1], 15}}})));
1087 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1088 // NOCP has credits remaining so will be passed on to host.
1089 EXPECT_EQ(capture.sends_called, 2);
1090 }
1091
TEST_F(NumberOfCompletedPacketsTest,EventUnmodifiedIfNoCreditsInUse)1092 TEST_F(NumberOfCompletedPacketsTest, EventUnmodifiedIfNoCreditsInUse) {
1093 constexpr size_t kNumConnections = 2;
1094 struct {
1095 int sends_called = 0;
1096 const std::array<uint16_t, kNumConnections> connection_handles = {0x123,
1097 0x456};
1098 } capture;
1099
1100 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1101 [&capture](H4PacketWithHci&& packet) {
1102 PW_TEST_ASSERT_OK_AND_ASSIGN(
1103 auto event_header,
1104 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1105 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1106 capture.sends_called++;
1107 if (event_header.event_code().Read() !=
1108 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1109 return;
1110 }
1111
1112 PW_TEST_ASSERT_OK_AND_ASSIGN(
1113 auto view,
1114 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1115 packet.GetHciSpan()));
1116 EXPECT_EQ(packet.GetHciSpan().size(), 11ul);
1117 EXPECT_EQ(view.num_handles().Read(), 2);
1118 EXPECT_EQ(view.header().event_code().Read(),
1119 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1120
1121 // Event should be unmodified.
1122 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1123 capture.connection_handles[0]);
1124 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 10);
1125 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
1126 capture.connection_handles[1]);
1127 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 15);
1128 });
1129 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1130 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1131
1132 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1133 std::move(send_to_controller_fn),
1134 /*le_acl_credits_to_reserve=*/10,
1135 /*br_edr_acl_credits_to_reserve=*/0);
1136 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 10));
1137 EXPECT_EQ(capture.sends_called, 1);
1138
1139 // Send Number_of_Completed_Packets event that reports 10 packets on
1140 // Connection 0 and 15 packets on Connection 1. Checks in send_to_host_fn
1141 // will ensure we have not modified the NOCP event.
1142 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1143 proxy,
1144 FlatMap<uint16_t, uint16_t, 2>({{{capture.connection_handles[0], 10},
1145 {capture.connection_handles[1], 15}}})));
1146 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 10);
1147 // NOCP has credits remaining so will be passed on to host.
1148 EXPECT_EQ(capture.sends_called, 2);
1149 }
1150
TEST_F(NumberOfCompletedPacketsTest,HandlesUnusualEvents)1151 TEST_F(NumberOfCompletedPacketsTest, HandlesUnusualEvents) {
1152 constexpr size_t kNumConnections = 5;
1153 struct {
1154 int sends_called = 0;
1155 const std::array<uint16_t, kNumConnections> connection_handles = {
1156 0x123, 0x234, 0x345, 0x456, 0x567};
1157 } capture;
1158
1159 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1160 [&capture](H4PacketWithHci&& packet) {
1161 PW_TEST_ASSERT_OK_AND_ASSIGN(
1162 auto event_header,
1163 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1164 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1165 capture.sends_called++;
1166 if (event_header.event_code().Read() !=
1167 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1168 return;
1169 }
1170
1171 PW_TEST_ASSERT_OK_AND_ASSIGN(
1172 auto view,
1173 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1174 packet.GetHciSpan()));
1175 if (view.num_handles().Read() == 0) {
1176 return;
1177 }
1178
1179 EXPECT_EQ(packet.GetHciSpan().size(), 23ul);
1180 EXPECT_EQ(view.num_handles().Read(), 5);
1181 EXPECT_EQ(view.header().event_code().Read(),
1182 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1183
1184 // Event should be unmodified.
1185 for (int i = 0; i < 5; ++i) {
1186 EXPECT_EQ(view.nocp_data()[i].connection_handle().Read(),
1187 capture.connection_handles[i]);
1188 EXPECT_EQ(view.nocp_data()[i].num_completed_packets().Read(), 0);
1189 }
1190 });
1191 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1192 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1193
1194 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1195 std::move(send_to_controller_fn),
1196 /*le_acl_credits_to_reserve=*/10,
1197 /*br_edr_acl_credits_to_reserve=*/0);
1198 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 10));
1199 EXPECT_EQ(capture.sends_called, 1);
1200
1201 // Send Number_of_Completed_Packets event with no entries.
1202 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1203 proxy, FlatMap<uint16_t, uint16_t, 0>({{}})));
1204 // NOCP has no entries, so will not be passed on to host.
1205 EXPECT_EQ(capture.sends_called, 1);
1206
1207 // Send Number_of_Completed_Packets event that reports 0 packets for various
1208 // connections.
1209 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1210 proxy,
1211 FlatMap<uint16_t, uint16_t, 5>({{{capture.connection_handles[0], 0},
1212 {capture.connection_handles[1], 0},
1213 {capture.connection_handles[2], 0},
1214 {capture.connection_handles[3], 0},
1215 {capture.connection_handles[4], 0}}})));
1216 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 10);
1217 // Proxy host will not pass on a NOCP with no credits.
1218 EXPECT_EQ(capture.sends_called, 1);
1219 }
1220
TEST_F(NumberOfCompletedPacketsTest,MultipleChannelsDifferentTransports)1221 TEST_F(NumberOfCompletedPacketsTest, MultipleChannelsDifferentTransports) {
1222 static constexpr size_t kPayloadSize = 3;
1223 struct {
1224 int sends_called = 0;
1225 std::array<uint8_t, kPayloadSize> payload = {
1226 0xAB,
1227 0xCD,
1228 0xEF,
1229 };
1230 } capture;
1231
1232 pw::Function<void(H4PacketWithHci&&)>&& send_to_host_fn(
1233 [](H4PacketWithHci&&) {});
1234 pw::Function<void(H4PacketWithH4&&)>&& send_to_controller_fn(
1235 [&capture](H4PacketWithH4&&) { ++capture.sends_called; });
1236
1237 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1238 std::move(send_to_controller_fn),
1239 /*le_acl_credits_to_reserve=*/1,
1240 /*br_edr_acl_credits_to_reserve=*/1);
1241 // Allow proxy to reserve BR/EDR 1 credit.
1242 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
1243 // Allow proxy to reserve LE 1 credit.
1244 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1245
1246 // Test that sending on one type of transport doesn't get blocked if the other
1247 // type of transport is out of credits.
1248
1249 L2capCoc le_channel =
1250 BuildCoc(proxy, CocParameters{.handle = 0x123, .tx_credits = 2});
1251 PW_TEST_EXPECT_OK(le_channel.Write(multibuf::MultiBuf{}).status);
1252 EXPECT_EQ(capture.sends_called, 1);
1253
1254 RfcommChannel bredr_channel =
1255 BuildRfcomm(proxy, RfcommParameters{.handle = 0x456});
1256 PW_TEST_EXPECT_OK(
1257 bredr_channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
1258 // Send should succeed even though no LE credits available
1259 EXPECT_EQ(capture.sends_called, 2);
1260
1261 // Queue an LE write
1262 PW_TEST_EXPECT_OK(le_channel.Write(multibuf::MultiBuf{}).status);
1263 EXPECT_EQ(capture.sends_called, 2);
1264
1265 // Complete previous LE write
1266 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1267 proxy, FlatMap<uint16_t, uint16_t, 1>({{{0x123, 1}}})));
1268 EXPECT_EQ(capture.sends_called, 3);
1269
1270 // Complete BR/EDR write
1271 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1272 proxy, FlatMap<uint16_t, uint16_t, 1>({{{0x456, 1}}})));
1273
1274 // Write again
1275 PW_TEST_EXPECT_OK(
1276 bredr_channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
1277 EXPECT_EQ(capture.sends_called, 4);
1278 }
1279
1280 // ########## DisconnectionCompleteTest
1281
1282 class DisconnectionCompleteTest : public ProxyHostTest {};
1283
TEST_F(DisconnectionCompleteTest,DisconnectionReclaimsCredits)1284 TEST_F(DisconnectionCompleteTest, DisconnectionReclaimsCredits) {
1285 struct {
1286 int sends_called = 0;
1287 uint16_t connection_handle = 0x123;
1288 } capture;
1289
1290 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1291 [&capture](H4PacketWithHci&& packet) {
1292 PW_TEST_ASSERT_OK_AND_ASSIGN(
1293 auto event_header,
1294 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1295 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1296 capture.sends_called++;
1297 if (event_header.event_code().Read() !=
1298 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1299 return;
1300 }
1301
1302 PW_TEST_ASSERT_OK_AND_ASSIGN(
1303 auto view,
1304 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1305 packet.GetHciSpan()));
1306 EXPECT_EQ(packet.GetHciSpan().size(), 7ul);
1307 EXPECT_EQ(view.num_handles().Read(), 1);
1308 EXPECT_EQ(view.header().event_code().Read(),
1309 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1310
1311 // Event should be unmodified.
1312 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1313 capture.connection_handle);
1314 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 10);
1315 });
1316 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1317 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1318
1319 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1320 std::move(send_to_controller_fn),
1321 /*le_acl_credits_to_reserve=*/10,
1322 /*br_edr_acl_credits_to_reserve=*/0);
1323 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 10));
1324 EXPECT_EQ(capture.sends_called, 1);
1325
1326 std::array<uint8_t, 1> attribute_value = {0};
1327
1328 {
1329 GattNotifyChannel channel =
1330 BuildGattNotifyChannel(proxy, {.handle = capture.connection_handle});
1331
1332 // Use up 3 of the 10 credits on the Connection that will be disconnected.
1333 for (int i = 0; i < 3; ++i) {
1334 EXPECT_TRUE(
1335 channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1336 }
1337 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 7);
1338 }
1339
1340 // Use up 2 credits on a random Connection.
1341 {
1342 GattNotifyChannel channel = BuildGattNotifyChannel(proxy, {});
1343
1344 for (int i = 0; i < 2; ++i) {
1345 EXPECT_TRUE(
1346 channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1347 }
1348 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 5);
1349 }
1350
1351 // Send Disconnection_Complete event, which should reclaim 3 credits.
1352 PW_TEST_EXPECT_OK(
1353 SendDisconnectionCompleteEvent(proxy, capture.connection_handle));
1354 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 8);
1355
1356 // Use 1 credit and reclaim it on a bunch of random channels. Then send
1357 // disconnect and ensure it was cleaned up in connections list. The send will
1358 // fail if disconnect doesn't cleanup properly.
1359 //
1360 // We already have an active connection at this point in the test, so loop
1361 // over the remaining slots + 1 which would otherwise fail if cleanup wasn't
1362 // working right.
1363 for (uint16_t i = 0; i < ProxyHost::GetMaxNumAclConnections() - 2; ++i) {
1364 uint16_t handle = 0x234 + i;
1365 GattNotifyChannel channel =
1366 BuildGattNotifyChannel(proxy, {.handle = handle});
1367 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1368 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1369 proxy, FlatMap<uint16_t, uint16_t, 1>({{{handle, 1}}})));
1370 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 8);
1371 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, handle));
1372 }
1373
1374 // Send Number_of_Completed_Packets event that reports 10 packets, none of
1375 // which should be reclaimed because this Connection has disconnected. Checks
1376 // in send_to_host_fn will ensure we have not modified the NOCP event.
1377 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1378 proxy,
1379 FlatMap<uint16_t, uint16_t, 1>({{{capture.connection_handle, 10}}})));
1380 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 8);
1381 // NOCP has credits remaining so will be passed on to host.
1382 EXPECT_EQ(capture.sends_called, 11);
1383 }
1384
TEST_F(DisconnectionCompleteTest,FailedDisconnectionHasNoEffect)1385 TEST_F(DisconnectionCompleteTest, FailedDisconnectionHasNoEffect) {
1386 uint16_t connection_handle = 0x123;
1387
1388 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1389 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1390 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1391 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1392
1393 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1394 std::move(send_to_controller_fn),
1395 /*le_acl_credits_to_reserve=*/1,
1396 /*br_edr_acl_credits_to_reserve=*/0);
1397 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1398
1399 std::array<uint8_t, 1> attribute_value = {0};
1400
1401 // Use sole credit.
1402 GattNotifyChannel channel =
1403 BuildGattNotifyChannel(proxy, {.handle = connection_handle});
1404 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1405 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1406
1407 // Send failed Disconnection_Complete event, should not reclaim credit.
1408 PW_TEST_EXPECT_OK(
1409 SendDisconnectionCompleteEvent(proxy,
1410 connection_handle,
1411 /*direction=*/Direction::kFromController,
1412 /*successful=*/false));
1413 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1414 }
1415
TEST_F(DisconnectionCompleteTest,DisconnectionOfUnusedConnectionHasNoEffect)1416 TEST_F(DisconnectionCompleteTest, DisconnectionOfUnusedConnectionHasNoEffect) {
1417 uint16_t connection_handle = 0x123;
1418
1419 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1420 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1421 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1422 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1423
1424 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1425 std::move(send_to_controller_fn),
1426 /*le_acl_credits_to_reserve=*/1,
1427 /*br_edr_acl_credits_to_reserve=*/0);
1428 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1429
1430 std::array<uint8_t, 1> attribute_value = {0};
1431
1432 // Use sole credit.
1433 GattNotifyChannel channel =
1434 BuildGattNotifyChannel(proxy, {.handle = connection_handle});
1435 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1436 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1437
1438 // Send Disconnection_Complete event to random Connection, should have no
1439 // effect.
1440 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, 0x456));
1441 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1442 }
1443
TEST_F(DisconnectionCompleteTest,CanReuseConnectionHandleAfterDisconnection)1444 TEST_F(DisconnectionCompleteTest, CanReuseConnectionHandleAfterDisconnection) {
1445 struct {
1446 int sends_called = 0;
1447 uint16_t connection_handle = 0x123;
1448 } capture;
1449
1450 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1451 [&capture](H4PacketWithHci&& packet) {
1452 PW_TEST_ASSERT_OK_AND_ASSIGN(
1453 auto event_header,
1454 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1455 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1456 capture.sends_called++;
1457 if (event_header.event_code().Read() !=
1458 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1459 return;
1460 }
1461
1462 PW_TEST_ASSERT_OK_AND_ASSIGN(
1463 auto view,
1464 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1465 packet.GetHciSpan()));
1466 EXPECT_EQ(packet.GetHciSpan().size(), 7ul);
1467 EXPECT_EQ(view.num_handles().Read(), 1);
1468 EXPECT_EQ(view.header().event_code().Read(),
1469 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1470
1471 // Should have reclaimed the 1 packet.
1472 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1473 capture.connection_handle);
1474 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 0);
1475 });
1476 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1477 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1478
1479 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1480 std::move(send_to_controller_fn),
1481 /*le_acl_credits_to_reserve=*/1,
1482 /*br_edr_acl_credits_to_reserve=*/0);
1483 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1484 EXPECT_EQ(capture.sends_called, 1);
1485
1486 std::array<uint8_t, 1> attribute_value = {0};
1487
1488 {
1489 // Establish connection over `connection_handle`.
1490 GattNotifyChannel channel =
1491 BuildGattNotifyChannel(proxy, {.handle = capture.connection_handle});
1492 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1493 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1494 }
1495
1496 // Disconnect `connection_handle`.
1497 PW_TEST_EXPECT_OK(
1498 SendDisconnectionCompleteEvent(proxy, capture.connection_handle));
1499 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1500 EXPECT_EQ(capture.sends_called, 2);
1501
1502 {
1503 // Re-establish connection over `connection_handle`.
1504 GattNotifyChannel channel =
1505 BuildGattNotifyChannel(proxy, {.handle = capture.connection_handle});
1506 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1507 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1508 }
1509
1510 // Send Number_of_Completed_Packets event that reports 1 packet. Checks in
1511 // send_to_host_fn will ensure packet has been reclaimed.
1512 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1513 proxy,
1514 FlatMap<uint16_t, uint16_t, 1>({{{capture.connection_handle, 1}}})));
1515 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1516 // Since proxy reclaimed the one credit, it does not pass event on to host.
1517 EXPECT_EQ(capture.sends_called, 2);
1518 }
1519
TEST_F(DisconnectionCompleteTest,DisconnectionErasesAclConnection)1520 TEST_F(DisconnectionCompleteTest, DisconnectionErasesAclConnection) {
1521 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1522 [](H4PacketWithHci&&) {});
1523 int sends_called = 0;
1524 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1525 [&sends_called](H4PacketWithH4&&) { ++sends_called; });
1526 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1527 std::move(send_to_controller_fn),
1528 /*le_acl_credits_to_reserve=*/1,
1529 /*br_edr_acl_credits_to_reserve=*/0);
1530 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1531
1532 uint16_t connection_handle = 0x567;
1533 pw::Vector<L2capCoc, ProxyHost::GetMaxNumAclConnections()> channels;
1534 for (size_t i = 0; i < ProxyHost::GetMaxNumAclConnections(); ++i) {
1535 channels.push_back(
1536 BuildCoc(proxy, CocParameters{.handle = ++connection_handle}));
1537 }
1538 EXPECT_EQ(
1539 BuildCocWithResult(
1540 proxy,
1541 CocParameters{.handle = static_cast<uint16_t>(connection_handle + 1)})
1542 .status(),
1543 Status::Unavailable());
1544
1545 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, connection_handle++));
1546 // After erasing the last ACL connection, there should be space for a new one.
1547 PW_TEST_ASSERT_OK_AND_ASSIGN(
1548 L2capCoc channel,
1549 BuildCocWithResult(proxy, CocParameters{.handle = connection_handle}));
1550 // Confirm signaling channels are functional.
1551 PW_TEST_EXPECT_OK(channel.SendAdditionalRxCredits(3));
1552 EXPECT_EQ(sends_called, 1);
1553 channels.clear();
1554 }
1555
1556 // ########## DestructionTest
1557
1558 class DestructionTest : public ProxyHostTest {};
1559
1560 // This test can deadlock on failure.
TEST_F(DestructionTest,CanDestructWhenPacketsQueuedInSignalingChannel)1561 TEST_F(DestructionTest, CanDestructWhenPacketsQueuedInSignalingChannel) {
1562 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1563 [](H4PacketWithHci&&) {});
1564 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1565 [](H4PacketWithH4&&) {});
1566 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1567 std::move(send_to_controller_fn),
1568 /*le_acl_credits_to_reserve=*/0,
1569 /*br_edr_acl_credits_to_reserve=*/0);
1570
1571 L2capCoc channel = BuildCoc(proxy, CocParameters{.handle = 0x111});
1572 L2capCoc channel2 = BuildCoc(proxy, CocParameters{.handle = 0x222});
1573
1574 PW_TEST_EXPECT_OK(channel.SendAdditionalRxCredits(1));
1575 }
1576
TEST_F(DestructionTest,ChannelsStopOnProxyDestruction)1577 TEST_F(DestructionTest, ChannelsStopOnProxyDestruction) {
1578 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1579 [](H4PacketWithHci&&) {});
1580 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1581 [](H4PacketWithH4&&) {});
1582 size_t events_received = 0;
1583
1584 pw::Vector<ProxyHost, 1> proxy;
1585 proxy.emplace_back(std::move(send_to_host_fn),
1586 std::move(send_to_controller_fn),
1587 /*le_acl_credits_to_reserve=*/0,
1588 /*br_edr_acl_credits_to_reserve=*/0);
1589
1590 // This event function will be called by each of the channels' event
1591 // functions.
1592 ChannelEventCallback shared_event_fn =
1593 [&events_received](L2capChannelEvent event) {
1594 ++events_received;
1595 EXPECT_EQ(event, L2capChannelEvent::kChannelClosedByOther);
1596 };
1597
1598 BasicL2capChannel close_first_channel = BuildBasicL2capChannel(
1599 proxy.front(),
1600 BasicL2capParameters{
1601 .event_fn = [&shared_event_fn](L2capChannelEvent event) {
1602 shared_event_fn(event);
1603 }});
1604
1605 OneOfEachChannel channel_struct =
1606 BuildOneOfEachChannel(proxy.front(), shared_event_fn);
1607
1608 // Channel already closed before Proxy destruction should not be affected.
1609 close_first_channel.Close();
1610 EXPECT_EQ(events_received, 1ul);
1611 EXPECT_EQ(close_first_channel.state(), L2capChannel::State::kClosed);
1612
1613 // Proxy dtor should result in close event for each of
1614 // the previously still open channels (and they should now be closed).
1615 proxy.clear();
1616 EXPECT_EQ(events_received, 1 + channel_struct.AllChannels().size());
1617 for (L2capChannel* channel : channel_struct.AllChannels()) {
1618 EXPECT_EQ(channel->state(), L2capChannel::State::kClosed);
1619 }
1620
1621 // And first channel should remain closed of course.
1622 EXPECT_EQ(close_first_channel.state(), L2capChannel::State::kClosed);
1623 }
1624
1625 // ########## ResetTest
1626
1627 class ResetTest : public ProxyHostTest {};
1628
TEST_F(ResetTest,ResetClearsActiveConnections)1629 TEST_F(ResetTest, ResetClearsActiveConnections) {
1630 struct {
1631 int sends_called = 0;
1632 const uint16_t connection_handle = 0x123;
1633 } host_capture;
1634 struct {
1635 int sends_called = 0;
1636 const uint16_t connection_handle = 0x123;
1637 } controller_capture;
1638
1639 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1640 [&host_capture](H4PacketWithHci&& packet) {
1641 PW_TEST_ASSERT_OK_AND_ASSIGN(
1642 auto event_header,
1643 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1644 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1645 host_capture.sends_called++;
1646 if (event_header.event_code().Read() !=
1647 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1648 return;
1649 }
1650
1651 PW_TEST_ASSERT_OK_AND_ASSIGN(
1652 auto view,
1653 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1654 packet.GetHciSpan()));
1655 EXPECT_EQ(packet.GetHciSpan().size(), 7ul);
1656 EXPECT_EQ(view.num_handles().Read(), 1);
1657 EXPECT_EQ(view.header().event_code().Read(),
1658 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1659
1660 // Should be unchanged.
1661 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1662 host_capture.connection_handle);
1663 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 1);
1664 });
1665 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1666 [&controller_capture]([[maybe_unused]] H4PacketWithH4&& packet) {
1667 ++controller_capture.sends_called;
1668 });
1669
1670 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1671 std::move(send_to_controller_fn),
1672 /*le_acl_credits_to_reserve=*/2,
1673 /*br_edr_acl_credits_to_reserve=*/0);
1674 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1675 EXPECT_EQ(host_capture.sends_called, 1);
1676
1677 std::array<uint8_t, 1> attribute_value = {0};
1678
1679 {
1680 GattNotifyChannel channel = BuildGattNotifyChannel(
1681 proxy, {.handle = controller_capture.connection_handle});
1682 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1683 EXPECT_EQ(controller_capture.sends_called, 1);
1684 }
1685
1686 proxy.Reset();
1687
1688 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1689 // Reset should not have cleared `le_acl_credits_to_reserve`, so proxy should
1690 // still indicate the capability.
1691 EXPECT_TRUE(proxy.HasSendLeAclCapability());
1692
1693 // Re-initialize AclDataChannel with 2 credits.
1694 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1695 EXPECT_EQ(host_capture.sends_called, 2);
1696
1697 {
1698 // Send ACL on random handle to expend one credit.
1699 GattNotifyChannel channel = BuildGattNotifyChannel(proxy, {});
1700 EXPECT_TRUE(channel.Write(MultiBufFromArray(attribute_value)).status.ok());
1701 EXPECT_EQ(controller_capture.sends_called, 2);
1702 }
1703
1704 // This should have no effect, as the reset has cleared our active connection
1705 // on this handle.
1706 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1707 proxy,
1708 FlatMap<uint16_t, uint16_t, 1>({{{host_capture.connection_handle, 1}}})));
1709 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1710 // NOCP has credits remaining so will be passed on to host.
1711 EXPECT_EQ(host_capture.sends_called, 3);
1712 }
1713
TEST_F(ResetTest,ChannelsCloseOnReset)1714 TEST_F(ResetTest, ChannelsCloseOnReset) {
1715 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1716 [](H4PacketWithHci&&) {});
1717 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1718 [](H4PacketWithH4&&) {});
1719 size_t events_received = 0;
1720 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1721 std::move(send_to_controller_fn),
1722 /*le_acl_credits_to_reserve=*/0,
1723 /*br_edr_acl_credits_to_reserve=*/0);
1724
1725 // This event function will be called by each of the channels' event
1726 // functions.
1727 ChannelEventCallback shared_event_fn =
1728 [&events_received](L2capChannelEvent event) {
1729 if (++events_received == 1) {
1730 EXPECT_EQ(event, L2capChannelEvent::kChannelClosedByOther);
1731 } else {
1732 EXPECT_EQ(event, L2capChannelEvent::kReset);
1733 }
1734 };
1735
1736 BasicL2capChannel close_first_channel = BuildBasicL2capChannel(
1737 proxy,
1738 BasicL2capParameters{
1739 .event_fn = [&shared_event_fn](L2capChannelEvent event) {
1740 shared_event_fn(event);
1741 }});
1742
1743 OneOfEachChannel channel_struct =
1744 BuildOneOfEachChannel(proxy, shared_event_fn);
1745
1746 // Channel already closed before Proxy reset should not be affected.
1747 close_first_channel.Close();
1748 EXPECT_EQ(events_received, 1ul);
1749 EXPECT_EQ(close_first_channel.state(), L2capChannel::State::kClosed);
1750
1751 // Proxy reset should result in close event for each of
1752 // the previously still open channels (and they should now be closed).
1753 proxy.Reset();
1754 EXPECT_EQ(events_received, 1 + channel_struct.AllChannels().size());
1755 for (L2capChannel* channel : channel_struct.AllChannels()) {
1756 EXPECT_EQ(channel->state(), L2capChannel::State::kClosed);
1757 }
1758
1759 // And first channel should remain closed of course.
1760 EXPECT_EQ(close_first_channel.state(), L2capChannel::State::kClosed);
1761 }
1762
TEST_F(ResetTest,ProxyHandlesMultipleResets)1763 TEST_F(ResetTest, ProxyHandlesMultipleResets) {
1764 int sends_called = 0;
1765
1766 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1767 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1768 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1769 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
1770 ++sends_called;
1771 });
1772
1773 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1774 std::move(send_to_controller_fn),
1775 /*le_acl_credits_to_reserve=*/1,
1776 /*br_edr_acl_credits_to_reserve=*/0);
1777 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1778
1779 proxy.Reset();
1780 proxy.Reset();
1781
1782 std::array<uint8_t, 1> attribute_value = {0};
1783 // Validate state after double reset.
1784 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1785 EXPECT_TRUE(proxy.HasSendLeAclCapability());
1786 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1787 {
1788 GattNotifyChannel channel = BuildGattNotifyChannel(proxy, {});
1789 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1790 PW_STATUS_OK);
1791 }
1792 EXPECT_EQ(sends_called, 1);
1793
1794 proxy.Reset();
1795
1796 // Validate state after third reset.
1797 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1798 EXPECT_TRUE(proxy.HasSendLeAclCapability());
1799 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1800 {
1801 GattNotifyChannel channel = BuildGattNotifyChannel(proxy, {});
1802 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1803 PW_STATUS_OK);
1804 }
1805 EXPECT_EQ(sends_called, 2);
1806 }
1807
TEST_F(ResetTest,HandleHciReset)1808 TEST_F(ResetTest, HandleHciReset) {
1809 struct {
1810 int sends_called = 0;
1811 const uint16_t connection_handle = 0x123;
1812 } host_capture;
1813 struct {
1814 int sends_called = 0;
1815 const uint16_t connection_handle = 0x123;
1816 } controller_capture;
1817
1818 pw::Function<void(H4PacketWithHci&&)> send_to_host_fn(
1819 [&host_capture](H4PacketWithHci&&) { ++host_capture.sends_called; });
1820 pw::Function<void(H4PacketWithH4&&)> send_to_controller_fn(
1821 [&controller_capture](H4PacketWithH4&&) {
1822 ++controller_capture.sends_called;
1823 });
1824
1825 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1826 std::move(send_to_controller_fn),
1827 /*le_acl_credits_to_reserve=*/2,
1828 /*br_edr_acl_credits_to_reserve=*/0);
1829 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1830 EXPECT_EQ(host_capture.sends_called, 1);
1831
1832 // Use 1 credit.
1833 std::array<uint8_t, 1> attribute_value = {0};
1834 GattNotifyChannel channel = BuildGattNotifyChannel(
1835 proxy, {.handle = controller_capture.connection_handle});
1836 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1837 PW_STATUS_OK);
1838 EXPECT_EQ(controller_capture.sends_called, 1);
1839 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1840
1841 // Send HCI_Reset. This should cause proxy to reset and our free credits as
1842 // well.
1843 std::array<uint8_t, emboss::ResetCommandView::SizeInBytes() + 1>
1844 h4_array_from_host{};
1845 H4PacketWithH4 h4_packet_from_host{emboss::H4PacketType::UNKNOWN,
1846 h4_array_from_host};
1847 PW_TEST_EXPECT_OK(
1848 CreateAndPopulateToControllerView<emboss::ResetCommandWriter>(
1849 h4_packet_from_host,
1850 emboss::OpCode::RESET,
1851 /*parameter_total_size=*/0));
1852 proxy.HandleH4HciFromHost(std::move(h4_packet_from_host));
1853
1854 // Send new buffer response which shouldn't crash.
1855 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1856 EXPECT_EQ(host_capture.sends_called, 2);
1857 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1858 }
1859
1860 // ########## MultiSendTest
1861
1862 class MultiSendTest : public ProxyHostTest {};
1863
TEST_F(MultiSendTest,CanOccupyAllThenReuseEachBuffer)1864 TEST_F(MultiSendTest, CanOccupyAllThenReuseEachBuffer) {
1865 constexpr size_t kMaxSends = ProxyHost::GetNumSimultaneousAclSendsSupported();
1866 struct {
1867 size_t sends_called = 0;
1868 std::array<H4PacketWithH4, 2 * kMaxSends> released_packets{};
1869 } capture;
1870
1871 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1872 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1873 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1874 [&capture](H4PacketWithH4&& packet) {
1875 // Capture all packets to prevent their destruction.
1876 capture.released_packets[capture.sends_called++] = std::move(packet);
1877 });
1878
1879 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1880 std::move(send_to_controller_fn),
1881 /*le_acl_credits_to_reserve=*/2 * kMaxSends,
1882 /*br_edr_acl_credits_to_reserve=*/0);
1883 // Allow proxy to reserve enough credits to send twice the number of
1884 // simultaneous sends supported by proxy.
1885 PW_TEST_EXPECT_OK(
1886 SendLeReadBufferResponseFromController(proxy, 2 * kMaxSends));
1887
1888 GattNotifyChannel channel = BuildGattNotifyChannel(proxy, {});
1889
1890 std::array<uint8_t, 1> attribute_value = {0xF};
1891 // Occupy all send buffers.
1892 for (size_t i = 0; i < kMaxSends; ++i) {
1893 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1894 PW_STATUS_OK);
1895 }
1896 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), kMaxSends);
1897 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1898 PW_STATUS_UNAVAILABLE);
1899
1900 // Confirm we can release and reoccupy each buffer slot.
1901 for (size_t i = 0; i < kMaxSends; ++i) {
1902 capture.released_packets[i].~H4PacketWithH4();
1903 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1904 PW_STATUS_OK);
1905 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1906 PW_STATUS_UNAVAILABLE);
1907 }
1908 EXPECT_EQ(capture.sends_called, 2 * kMaxSends);
1909
1910 // If captured packets are not reset here, they may destruct after the proxy
1911 // and lead to a crash when trying to lock the proxy's destructed mutex.
1912 for (auto& packet : capture.released_packets) {
1913 packet.ResetAndReturnReleaseFn();
1914 }
1915 }
1916
TEST_F(MultiSendTest,CanRepeatedlyReuseOneBuffer)1917 TEST_F(MultiSendTest, CanRepeatedlyReuseOneBuffer) {
1918 constexpr size_t kMaxSends = ProxyHost::GetNumSimultaneousAclSendsSupported();
1919 struct {
1920 size_t sends_called = 0;
1921 std::array<H4PacketWithH4, kMaxSends> released_packets{};
1922 } capture;
1923
1924 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1925 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1926 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1927 [&capture](H4PacketWithH4&& packet) {
1928 // Capture first kMaxSends packets linearly.
1929 if (capture.sends_called < capture.released_packets.size()) {
1930 capture.released_packets[capture.sends_called] = std::move(packet);
1931 } else {
1932 // Reuse only first packet slot after kMaxSends.
1933 capture.released_packets[0] = std::move(packet);
1934 }
1935 ++capture.sends_called;
1936 });
1937
1938 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1939 std::move(send_to_controller_fn),
1940 /*le_acl_credits_to_reserve=*/2 * kMaxSends,
1941 /*br_edr_acl_credits_to_reserve=*/0);
1942 PW_TEST_EXPECT_OK(
1943 SendLeReadBufferResponseFromController(proxy, 2 * kMaxSends));
1944
1945 GattNotifyChannel channel = BuildGattNotifyChannel(proxy, {});
1946
1947 std::array<uint8_t, 1> attribute_value = {0xF};
1948 // Occupy all send buffers.
1949 for (size_t i = 0; i < kMaxSends; ++i) {
1950 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1951 PW_STATUS_OK);
1952 }
1953
1954 // Repeatedly free and reoccupy first buffer.
1955 for (size_t i = 0; i < kMaxSends; ++i) {
1956 capture.released_packets[0].~H4PacketWithH4();
1957 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1958 PW_STATUS_OK);
1959 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1960 PW_STATUS_UNAVAILABLE);
1961 }
1962 EXPECT_EQ(capture.sends_called, 2 * kMaxSends);
1963
1964 // If captured packets are not reset here, they may destruct after the proxy
1965 // and lead to a crash when trying to lock the proxy's destructed mutex.
1966 for (auto& packet : capture.released_packets) {
1967 packet.ResetAndReturnReleaseFn();
1968 }
1969 }
1970
TEST_F(MultiSendTest,CanSendOverManyDifferentConnections)1971 TEST_F(MultiSendTest, CanSendOverManyDifferentConnections) {
1972 std::array<uint8_t, 1> attribute_value = {0xF};
1973 struct {
1974 uint16_t sends_called = 0;
1975 } capture;
1976
1977 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1978 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1979 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1980 [&capture]([[maybe_unused]] H4PacketWithH4&& packet) {
1981 ++capture.sends_called;
1982 });
1983
1984 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1985 std::move(send_to_controller_fn),
1986 ProxyHost::GetMaxNumAclConnections(),
1987 /*br_edr_acl_credits_to_reserve=*/0);
1988
1989 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
1990 proxy, ProxyHost::GetMaxNumAclConnections()));
1991
1992 for (uint16_t send = 1; send <= ProxyHost::GetMaxNumAclConnections();
1993 send++) {
1994 // Use current send count as the connection handle.
1995 uint16_t conn_handle = send;
1996 GattNotifyChannel channel =
1997 BuildGattNotifyChannel(proxy, {.handle = conn_handle});
1998 EXPECT_EQ(channel.Write(MultiBufFromArray(attribute_value)).status,
1999 PW_STATUS_OK);
2000 EXPECT_EQ(capture.sends_called, send);
2001 }
2002 }
2003
TEST_F(MultiSendTest,AttemptToCreateOverMaxConnectionsFails)2004 TEST_F(MultiSendTest, AttemptToCreateOverMaxConnectionsFails) {
2005 constexpr uint16_t kSends = ProxyHost::GetMaxNumAclConnections() + 1;
2006
2007 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2008 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2009 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2010 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2011
2012 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2013 std::move(send_to_controller_fn),
2014 /*le_acl_credits_to_reserve=*/kSends,
2015 /*br_edr_acl_credits_to_reserve=*/0);
2016
2017 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, kSends));
2018
2019 std::vector<GattNotifyChannel> channels;
2020
2021 for (uint16_t send = 1; send <= ProxyHost::GetMaxNumAclConnections();
2022 send++) {
2023 // Use current send count as the connection handle.
2024 uint16_t conn_handle = send;
2025 GattNotifyChannel channel =
2026 BuildGattNotifyChannel(proxy, {.handle = conn_handle});
2027 channels.push_back(std::move(channel));
2028 }
2029
2030 // Last one should fail
2031 EXPECT_EQ(
2032 BuildGattNotifyChannelWithResult(proxy, {.handle = kSends}).status(),
2033 Status::Unavailable());
2034 }
2035
2036 // ########## BasicL2capChannelTest
2037
2038 class BasicL2capChannelTest : public ProxyHostTest {};
2039
TEST_F(BasicL2capChannelTest,BasicWrite)2040 TEST_F(BasicL2capChannelTest, BasicWrite) {
2041 struct {
2042 int sends_called = 0;
2043 // First four bits 0x0 encode PB & BC flags
2044 uint16_t handle = 0x0ACB;
2045 // Length of L2CAP PDU
2046 uint16_t acl_data_total_length = 0x0007;
2047 // L2CAP header PDU length field
2048 uint16_t pdu_length = 0x0003;
2049 // Random CID
2050 uint16_t channel_id = 0x1234;
2051 // L2CAP information payload
2052 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
2053
2054 // Built from the preceding values in little endian order (except payload in
2055 // big endian).
2056 std::array<uint8_t, 11> expected_hci_packet = {
2057 0xCB, 0x0A, 0x07, 0x00, 0x03, 0x00, 0x34, 0x12, 0xAB, 0xCD, 0xEF};
2058 } capture;
2059
2060 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2061 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2062 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2063 [&capture](H4PacketWithH4&& packet) {
2064 ++capture.sends_called;
2065 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
2066 EXPECT_EQ(packet.GetHciSpan().size(),
2067 capture.expected_hci_packet.size());
2068 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
2069 packet.GetHciSpan().end(),
2070 capture.expected_hci_packet.begin(),
2071 capture.expected_hci_packet.end()));
2072 PW_TEST_ASSERT_OK_AND_ASSIGN(
2073 auto acl,
2074 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
2075 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
2076 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
2077 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
2078 EXPECT_EQ(acl.header().broadcast_flag().Read(),
2079 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
2080 EXPECT_EQ(acl.data_total_length().Read(),
2081 capture.acl_data_total_length);
2082 emboss::BFrameView bframe = emboss::MakeBFrameView(
2083 acl.payload().BackingStorage().data(), acl.SizeInBytes());
2084 EXPECT_EQ(bframe.pdu_length().Read(), capture.pdu_length);
2085 EXPECT_EQ(bframe.channel_id().Read(), capture.channel_id);
2086 for (size_t i = 0; i < 3; ++i) {
2087 EXPECT_EQ(bframe.payload()[i].Read(), capture.payload[i]);
2088 }
2089 });
2090
2091 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2092 std::move(send_to_controller_fn),
2093 /*le_acl_credits_to_reserve=*/1,
2094 /*br_edr_acl_credits_to_reserve=*/0);
2095 // Allow proxy to reserve 1 LE credit.
2096 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2097
2098 BasicL2capChannel channel =
2099 BuildBasicL2capChannel(proxy,
2100 {.handle = capture.handle,
2101 .local_cid = 0x123,
2102 .remote_cid = capture.channel_id,
2103 .transport = AclTransportType::kLe});
2104
2105 PW_TEST_EXPECT_OK(
2106 channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
2107 EXPECT_EQ(capture.sends_called, 1);
2108 }
2109
TEST_F(BasicL2capChannelTest,ErrorOnWriteTooLarge)2110 TEST_F(BasicL2capChannelTest, ErrorOnWriteTooLarge) {
2111 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2112 [](H4PacketWithHci&&) {});
2113 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2114 [](H4PacketWithH4&&) { FAIL(); });
2115
2116 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2117 std::move(send_to_controller_fn),
2118 /*le_acl_credits_to_reserve=*/1,
2119 /*br_edr_acl_credits_to_reserve=*/0);
2120 // Allow proxy to reserve 1 credit.
2121 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
2122
2123 std::array<uint8_t,
2124 ProxyHost::GetMaxAclSendSize() -
2125 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() -
2126 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + 1>
2127 hci_arr;
2128
2129 BasicL2capChannel channel =
2130 BuildBasicL2capChannel(proxy,
2131 {.handle = 0x123,
2132 .local_cid = 0x123,
2133 .remote_cid = 0x123,
2134 .transport = AclTransportType::kLe});
2135
2136 EXPECT_EQ(channel.Write(MultiBufFromSpan(pw::span(hci_arr))).status,
2137 PW_STATUS_INVALID_ARGUMENT);
2138 }
2139
TEST_F(BasicL2capChannelTest,CannotCreateChannelWithInvalidArgs)2140 TEST_F(BasicL2capChannelTest, CannotCreateChannelWithInvalidArgs) {
2141 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2142 [](H4PacketWithHci&&) {});
2143 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2144 [](H4PacketWithH4&&) {});
2145
2146 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2147 std::move(send_to_controller_fn),
2148 /*le_acl_credits_to_reserve=*/0,
2149 /*br_edr_acl_credits_to_reserve=*/0);
2150
2151 // Connection handle too large by 1.
2152
2153 Result<BasicL2capChannel> channel =
2154 BuildBasicL2capChannelWithResult(proxy,
2155 {.handle = 0x0FFF,
2156 .local_cid = 0x123,
2157 .remote_cid = 0x123,
2158 .transport = AclTransportType::kLe});
2159 EXPECT_EQ(channel.status(), Status::InvalidArgument());
2160
2161 // Local CID invalid (0).
2162 channel =
2163 BuildBasicL2capChannelWithResult(proxy,
2164 BasicL2capParameters{
2165 .handle = 0x123,
2166 .local_cid = 0,
2167 .remote_cid = 0x123,
2168 .transport = AclTransportType::kLe,
2169 });
2170 EXPECT_EQ(channel.status(), Status::InvalidArgument());
2171 }
2172
TEST_F(BasicL2capChannelTest,BasicRead)2173 TEST_F(BasicL2capChannelTest, BasicRead) {
2174 struct {
2175 int sends_called = 0;
2176 int to_host_called = 0;
2177 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
2178 } capture;
2179
2180 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2181 [&capture](H4PacketWithHci&&) { ++capture.to_host_called; });
2182 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2183 [](H4PacketWithH4&&) {});
2184 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2185 std::move(send_to_controller_fn),
2186 /*le_acl_credits_to_reserve=*/0,
2187 /*br_edr_acl_credits_to_reserve=*/0);
2188
2189 uint16_t handle = 334;
2190 uint16_t local_cid = 443;
2191 BasicL2capChannel channel = BuildBasicL2capChannel(
2192 proxy,
2193 BasicL2capParameters{
2194 .handle = handle,
2195 .local_cid = local_cid,
2196 .remote_cid = 0x123,
2197 .transport = AclTransportType::kLe,
2198 .payload_from_controller_fn =
2199 [&capture](multibuf::MultiBuf&& buffer) {
2200 ++capture.sends_called;
2201 std::optional<pw::ByteSpan> payload = buffer.ContiguousSpan();
2202 ConstByteSpan expected_bytes =
2203 as_bytes(span(capture.expected_payload.data(),
2204 capture.expected_payload.size()));
2205 EXPECT_TRUE(payload.has_value());
2206 EXPECT_TRUE(std::equal(payload->begin(),
2207 payload->end(),
2208 expected_bytes.begin(),
2209 expected_bytes.end()));
2210 return std::nullopt;
2211 },
2212 });
2213
2214 std::array<uint8_t,
2215 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2216 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2217 capture.expected_payload.size()>
2218 hci_arr;
2219 hci_arr.fill(0);
2220 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2221
2222 Result<emboss::AclDataFrameWriter> acl =
2223 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2224 acl->header().handle().Write(handle);
2225 acl->data_total_length().Write(
2226 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2227 capture.expected_payload.size());
2228
2229 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
2230 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
2231 bframe.pdu_length().Write(capture.expected_payload.size());
2232 bframe.channel_id().Write(local_cid);
2233 std::copy(capture.expected_payload.begin(),
2234 capture.expected_payload.end(),
2235 hci_arr.begin() +
2236 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2237 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
2238
2239 // Send ACL data packet destined for the CoC we registered.
2240 proxy.HandleH4HciFromController(std::move(h4_packet));
2241
2242 EXPECT_EQ(capture.sends_called, 1);
2243 EXPECT_EQ(capture.to_host_called, 0);
2244 }
2245
TEST_F(BasicL2capChannelTest,BasicForward)2246 TEST_F(BasicL2capChannelTest, BasicForward) {
2247 struct {
2248 int sends_called = 0;
2249 int to_host_called = 0;
2250 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
2251 std::array<uint8_t,
2252 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2253 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + 3>
2254 hci_arr{};
2255 } capture;
2256
2257 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, capture.hci_arr};
2258
2259 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2260 [&capture](H4PacketWithHci&& packet) {
2261 ++capture.to_host_called;
2262 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
2263 packet.GetHciSpan().end(),
2264 capture.hci_arr.begin(),
2265 capture.hci_arr.end()));
2266 });
2267 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2268 [](H4PacketWithH4&&) {});
2269 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2270 std::move(send_to_controller_fn),
2271 /*le_acl_credits_to_reserve=*/0,
2272 /*br_edr_acl_credits_to_reserve=*/0);
2273
2274 uint16_t handle = 334;
2275 uint16_t local_cid = 443;
2276 BasicL2capChannel channel =
2277 BuildBasicL2capChannel(proxy,
2278 BasicL2capParameters{
2279 .handle = handle,
2280 .local_cid = local_cid,
2281 .remote_cid = 0x123,
2282 .transport = AclTransportType::kLe,
2283 .payload_from_controller_fn =
2284 [&capture](multibuf::MultiBuf&& buffer) {
2285 ++capture.sends_called;
2286 // Forward to host.
2287 return std::move(buffer);
2288 },
2289 });
2290
2291 Result<emboss::AclDataFrameWriter> acl =
2292 MakeEmbossWriter<emboss::AclDataFrameWriter>(capture.hci_arr);
2293 acl->header().handle().Write(handle);
2294 acl->data_total_length().Write(
2295 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2296 capture.expected_payload.size());
2297
2298 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
2299 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
2300 bframe.pdu_length().Write(capture.expected_payload.size());
2301 bframe.channel_id().Write(local_cid);
2302 std::copy(capture.expected_payload.begin(),
2303 capture.expected_payload.end(),
2304 capture.hci_arr.begin() +
2305 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2306 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
2307
2308 // Send ACL data packet destined for the CoC we registered.
2309 proxy.HandleH4HciFromController(std::move(h4_packet));
2310
2311 EXPECT_EQ(capture.sends_called, 1);
2312 EXPECT_EQ(capture.to_host_called, 1);
2313 }
2314
TEST_F(BasicL2capChannelTest,ReadPacketToController)2315 TEST_F(BasicL2capChannelTest, ReadPacketToController) {
2316 struct {
2317 int sends_called = 0;
2318 int from_host_called = 0;
2319 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
2320 std::array<uint8_t,
2321 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2322 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + 3>
2323 hci_arr{};
2324 } capture;
2325
2326 std::array<uint8_t, sizeof(emboss::H4PacketType) + capture.hci_arr.size()>
2327 h4_arr;
2328 h4_arr[0] = cpp23::to_underlying(emboss::H4PacketType::ACL_DATA);
2329 H4PacketWithH4 h4_packet{h4_arr};
2330
2331 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2332 [](H4PacketWithHci&&) {});
2333 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2334 [&capture](H4PacketWithH4&& packet) {
2335 ++capture.from_host_called;
2336 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
2337 packet.GetHciSpan().end(),
2338 capture.hci_arr.begin(),
2339 capture.hci_arr.end()));
2340 });
2341 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2342 std::move(send_to_controller_fn),
2343 /*le_acl_credits_to_reserve=*/0,
2344 /*br_edr_acl_credits_to_reserve=*/0);
2345 uint16_t handle = 0x334;
2346 uint16_t local_cid = 0x443;
2347 uint16_t remote_cid = 0x123;
2348 BasicL2capChannel channel =
2349 BuildBasicL2capChannel(proxy,
2350 BasicL2capParameters{
2351 .handle = handle,
2352 .local_cid = local_cid,
2353 .remote_cid = remote_cid,
2354 .transport = AclTransportType::kBrEdr,
2355 .payload_from_host_fn =
2356 [&capture](multibuf::MultiBuf&& buffer) {
2357 ++capture.sends_called;
2358 return std::move(buffer);
2359 },
2360 });
2361
2362 Result<emboss::AclDataFrameWriter> acl =
2363 MakeEmbossWriter<emboss::AclDataFrameWriter>(capture.hci_arr);
2364 acl->header().handle().Write(handle);
2365 acl->data_total_length().Write(
2366 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2367 capture.expected_payload.size());
2368
2369 emboss::BasicL2capHeaderWriter l2cap_header =
2370 emboss::MakeBasicL2capHeaderView(
2371 acl->payload().BackingStorage().data(),
2372 acl->payload().BackingStorage().SizeInBytes());
2373 l2cap_header.pdu_length().Write(capture.expected_payload.size());
2374 l2cap_header.channel_id().Write(remote_cid);
2375
2376 std::copy(capture.expected_payload.begin(),
2377 capture.expected_payload.end(),
2378 capture.hci_arr.begin() +
2379 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2380 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
2381
2382 std::copy(capture.hci_arr.begin(), capture.hci_arr.end(), h4_arr.begin() + 1);
2383
2384 proxy.HandleH4HciFromHost(std::move(h4_packet));
2385
2386 EXPECT_EQ(capture.from_host_called, 1);
2387 EXPECT_EQ(capture.sends_called, 1);
2388 }
2389
2390 // ########## L2capSignalingTest
2391
2392 class L2capSignalingTest : public ProxyHostTest {};
2393
TEST_F(L2capSignalingTest,FlowControlCreditIndDrainsQueue)2394 TEST_F(L2capSignalingTest, FlowControlCreditIndDrainsQueue) {
2395 size_t sends_called = 0;
2396
2397 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2398 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2399 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2400 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
2401 ++sends_called;
2402 });
2403 ProxyHost proxy =
2404 ProxyHost(std::move(send_to_host_fn),
2405 std::move(send_to_controller_fn),
2406 /*le_acl_credits_to_reserve=*/L2capCoc::QueueCapacity(),
2407 /*br_edr_acl_credits_to_reserve=*/0);
2408 PW_TEST_EXPECT_OK(
2409 SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
2410 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), L2capCoc::QueueCapacity());
2411
2412 uint16_t handle = 123;
2413 uint16_t remote_cid = 456;
2414 L2capCoc channel = BuildCoc(
2415 proxy,
2416 CocParameters{
2417 .handle = handle, .remote_cid = remote_cid, .tx_credits = 0});
2418
2419 for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
2420 PW_TEST_EXPECT_OK(channel.Write(multibuf::MultiBuf{}).status);
2421 }
2422 EXPECT_EQ(channel.Write(multibuf::MultiBuf{}).status, Status::Unavailable());
2423 EXPECT_EQ(sends_called, 0u);
2424
2425 constexpr size_t kL2capLength =
2426 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2427 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes();
2428 constexpr size_t kHciLength =
2429 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
2430 std::array<uint8_t, kHciLength> hci_arr;
2431 hci_arr.fill(0);
2432 H4PacketWithHci flow_control_credit_ind{emboss::H4PacketType::ACL_DATA,
2433 pw::span(hci_arr.data(), kHciLength)};
2434
2435 Result<emboss::AclDataFrameWriter> acl =
2436 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2437 acl->header().handle().Write(handle);
2438 acl->data_total_length().Write(kL2capLength);
2439
2440 emboss::CFrameWriter l2cap = emboss::MakeCFrameView(
2441 acl->payload().BackingStorage().data(), kL2capLength);
2442 l2cap.pdu_length().Write(
2443 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2444 // 0x0005 = LE-U fixed signaling channel ID.
2445 l2cap.channel_id().Write(0x0005);
2446
2447 emboss::L2capFlowControlCreditIndWriter ind =
2448 emboss::MakeL2capFlowControlCreditIndView(
2449 l2cap.payload().BackingStorage().data(),
2450 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2451 ind.command_header().code().Write(
2452 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
2453 ind.command_header().data_length().Write(
2454 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
2455 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
2456 ind.cid().Write(remote_cid);
2457 ind.credits().Write(L2capCoc::QueueCapacity());
2458
2459 proxy.HandleH4HciFromController(std::move(flow_control_credit_ind));
2460
2461 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
2462 }
2463
TEST_F(L2capSignalingTest,ChannelClosedWithErrorIfCreditsExceeded)2464 TEST_F(L2capSignalingTest, ChannelClosedWithErrorIfCreditsExceeded) {
2465 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2466 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2467 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2468 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2469
2470 ProxyHost proxy =
2471 ProxyHost(std::move(send_to_host_fn),
2472 std::move(send_to_controller_fn),
2473 /*le_acl_credits_to_reserve=*/L2capCoc::QueueCapacity(),
2474 /*br_edr_acl_credits_to_reserve=*/0);
2475
2476 uint16_t handle = 123;
2477 uint16_t remote_cid = 456;
2478 int events_received = 0;
2479 L2capCoc channel = BuildCoc(
2480 proxy,
2481 CocParameters{
2482 .handle = handle,
2483 .remote_cid = remote_cid,
2484 // Initialize with max credit count.
2485 .tx_credits =
2486 emboss::L2capLeCreditBasedConnectionReq::max_credit_value(),
2487 .event_fn = [&events_received](L2capChannelEvent event) {
2488 EXPECT_EQ(event, L2capChannelEvent::kRxInvalid);
2489 ++events_received;
2490 }});
2491
2492 constexpr size_t kL2capLength =
2493 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2494 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes();
2495 constexpr size_t kHciLength =
2496 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
2497 std::array<uint8_t, kHciLength> hci_arr;
2498 hci_arr.fill(0);
2499 H4PacketWithHci flow_control_credit_ind{emboss::H4PacketType::ACL_DATA,
2500 pw::span(hci_arr.data(), kHciLength)};
2501
2502 Result<emboss::AclDataFrameWriter> acl =
2503 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2504 acl->header().handle().Write(handle);
2505 acl->data_total_length().Write(kL2capLength);
2506
2507 emboss::CFrameWriter l2cap =
2508 emboss::MakeCFrameView(acl->payload().BackingStorage().data(),
2509 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
2510 l2cap.pdu_length().Write(
2511 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2512 // 0x0005 = LE-U fixed signaling channel ID.
2513 l2cap.channel_id().Write(0x0005);
2514
2515 emboss::L2capFlowControlCreditIndWriter ind =
2516 emboss::MakeL2capFlowControlCreditIndView(
2517 l2cap.payload().BackingStorage().data(),
2518 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2519 ind.command_header().code().Write(
2520 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
2521 ind.command_header().data_length().Write(
2522 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
2523 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
2524 ind.cid().Write(remote_cid);
2525 // Exceed max credit count by 1.
2526 ind.credits().Write(1);
2527
2528 proxy.HandleH4HciFromController(std::move(flow_control_credit_ind));
2529
2530 EXPECT_EQ(events_received, 1);
2531 }
2532
TEST_F(L2capSignalingTest,SignalsArePassedOnToHost)2533 TEST_F(L2capSignalingTest, SignalsArePassedOnToHost) {
2534 int forwards_to_host = 0;
2535 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2536 [&forwards_to_host](H4PacketWithHci&&) { ++forwards_to_host; });
2537 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2538 [](H4PacketWithH4&&) {});
2539
2540 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2541 std::move(send_to_controller_fn),
2542 /*le_acl_credits_to_reserve=*/0,
2543 /*br_edr_acl_credits_to_reserve=*/0);
2544
2545 EXPECT_EQ(forwards_to_host, 0);
2546
2547 PW_TEST_EXPECT_OK(SendL2capConnectionReq(proxy, 44, 55, 56));
2548 EXPECT_EQ(forwards_to_host, 1);
2549 }
2550
TEST_F(L2capSignalingTest,SignalsArePassedOnToHostAfterAclDisconnect)2551 TEST_F(L2capSignalingTest, SignalsArePassedOnToHostAfterAclDisconnect) {
2552 uint16_t kConnHandle = 0x33;
2553 int sends_to_host = 0;
2554 int sends_to_controller = 0;
2555 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2556 [&sends_to_host](H4PacketWithHci&&) { ++sends_to_host; });
2557 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2558 [&sends_to_controller](H4PacketWithH4&&) { ++sends_to_controller; });
2559
2560 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2561 std::move(send_to_controller_fn),
2562 /*le_acl_credits_to_reserve=*/1,
2563 /*br_edr_acl_credits_to_reserve=*/0);
2564 // Allow proxy to reserve 1 credit.
2565 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2566 EXPECT_EQ(sends_to_host, 1);
2567
2568 // Send GATT Notify which should create ACL connection for kConnHandle.
2569 std::array<uint8_t, 1> attribute_value = {0};
2570 {
2571 GattNotifyChannel channel =
2572 BuildGattNotifyChannel(proxy, {.handle = kConnHandle});
2573 PW_TEST_EXPECT_OK(channel.Write(MultiBufFromArray(attribute_value)).status);
2574 }
2575 EXPECT_EQ(sends_to_controller, 1);
2576
2577 // Disconnect that connection.
2578 PW_TEST_EXPECT_OK(
2579 SendDisconnectionCompleteEvent(proxy, /*handle=*/kConnHandle));
2580 EXPECT_EQ(sends_to_host, 2);
2581
2582 // Send signal again using the same connection. Signal should be passed on
2583 // to host.
2584 PW_TEST_EXPECT_OK(
2585 SendL2capConnectionReq(proxy, /*handle=*/kConnHandle, 55, 56));
2586 EXPECT_EQ(sends_to_host, 3);
2587
2588 // Trigger credit send for L2capCoc to verify new signalling channel
2589 // object is present and working.
2590 {
2591 L2capCoc channel = BuildCoc(proxy, CocParameters{.handle = kConnHandle});
2592 PW_TEST_EXPECT_OK(channel.SendAdditionalRxCredits(7));
2593 }
2594 EXPECT_EQ(sends_to_controller, 2);
2595 }
2596
TEST_F(L2capSignalingTest,CreditIndAddressedToNonManagedChannelForwardedToHost)2597 TEST_F(L2capSignalingTest,
2598 CreditIndAddressedToNonManagedChannelForwardedToHost) {
2599 int forwards_to_host = 0;
2600 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2601 [&forwards_to_host](H4PacketWithHci&&) { ++forwards_to_host; });
2602 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2603 [](H4PacketWithH4&&) {});
2604
2605 ProxyHost proxy =
2606 ProxyHost(std::move(send_to_host_fn),
2607 std::move(send_to_controller_fn),
2608 /*le_acl_credits_to_reserve=*/L2capCoc::QueueCapacity(),
2609 /*br_edr_acl_credits_to_reserve=*/0);
2610
2611 uint16_t handle = 123;
2612 uint16_t remote_cid = 456;
2613 L2capCoc channel = BuildCoc(
2614 proxy, CocParameters{.handle = handle, .remote_cid = remote_cid});
2615
2616 constexpr size_t kL2capLength =
2617 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2618 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes();
2619 constexpr size_t kHciLength =
2620 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
2621 std::array<uint8_t, kHciLength> hci_arr;
2622 hci_arr.fill(0);
2623 H4PacketWithHci flow_control_credit_ind{emboss::H4PacketType::ACL_DATA,
2624 pw::span(hci_arr.data(), kHciLength)};
2625
2626 Result<emboss::AclDataFrameWriter> acl =
2627 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2628 acl->header().handle().Write(handle);
2629 acl->data_total_length().Write(kL2capLength);
2630
2631 emboss::CFrameWriter l2cap =
2632 emboss::MakeCFrameView(acl->payload().BackingStorage().data(),
2633 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
2634 l2cap.pdu_length().Write(
2635 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2636 // 0x0005 = LE-U fixed signaling channel ID.
2637 l2cap.channel_id().Write(0x0005);
2638
2639 emboss::L2capFlowControlCreditIndWriter ind =
2640 emboss::MakeL2capFlowControlCreditIndView(
2641 l2cap.payload().BackingStorage().data(),
2642 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2643 ind.command_header().code().Write(
2644 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
2645 ind.command_header().data_length().Write(
2646 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
2647 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
2648 // Address packet to different CID on same connection.
2649 ind.cid().Write(remote_cid + 1);
2650
2651 proxy.HandleH4HciFromController(std::move(flow_control_credit_ind));
2652
2653 EXPECT_EQ(forwards_to_host, 1);
2654 }
2655
TEST_F(L2capSignalingTest,RxAdditionalCreditsSent)2656 TEST_F(L2capSignalingTest, RxAdditionalCreditsSent) {
2657 struct {
2658 uint16_t handle = 123;
2659 uint16_t local_cid = 456;
2660 uint16_t credits = 3;
2661 int sends_called = 0;
2662 } capture;
2663
2664 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2665 [](H4PacketWithHci&&) {});
2666 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2667 [&capture](H4PacketWithH4&& packet) {
2668 ++capture.sends_called;
2669 PW_TEST_ASSERT_OK_AND_ASSIGN(
2670 auto acl,
2671 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
2672 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
2673 EXPECT_EQ(
2674 acl.data_total_length().Read(),
2675 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2676 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2677 emboss::CFrameView cframe = emboss::MakeCFrameView(
2678 acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
2679 EXPECT_EQ(cframe.pdu_length().Read(),
2680 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
2681 // 0x0005 = LE-U fixed signaling channel ID.
2682 EXPECT_EQ(cframe.channel_id().Read(), 0x0005);
2683 emboss::L2capFlowControlCreditIndView ind =
2684 emboss::MakeL2capFlowControlCreditIndView(
2685 cframe.payload().BackingStorage().data(),
2686 cframe.payload().SizeInBytes());
2687 EXPECT_EQ(ind.command_header().code().Read(),
2688 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
2689 // TODO: https://pwbug.dev/382553099 - Test to ensure we are properly
2690 // incrementing Identifier when sending multiple signaling packets.
2691 EXPECT_EQ(ind.command_header().identifier().Read(), 1);
2692 EXPECT_EQ(
2693 ind.command_header().data_length().Read(),
2694 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
2695 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
2696 EXPECT_EQ(ind.cid().Read(), capture.local_cid);
2697 EXPECT_EQ(ind.credits().Read(), capture.credits);
2698 });
2699
2700 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2701 std::move(send_to_controller_fn),
2702 /*le_acl_credits_to_reserve=*/1,
2703 /*br_edr_acl_credits_to_reserve=*/0);
2704 // Allow proxy to reserve 1 LE credit.
2705 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2706
2707 // Build channel so ACL connection is registered.
2708 L2capCoc channel = BuildCoc(
2709 proxy,
2710 CocParameters{.handle = capture.handle, .local_cid = capture.local_cid});
2711
2712 PW_TEST_EXPECT_OK(channel.SendAdditionalRxCredits(capture.credits));
2713
2714 EXPECT_EQ(capture.sends_called, 1);
2715 }
2716
2717 // ########## AcluSignalingChannelTest
2718
2719 class AcluSignalingChannelTest : public ProxyHostTest {};
2720
TEST_F(AcluSignalingChannelTest,HandlesMultipleCommands)2721 TEST_F(AcluSignalingChannelTest, HandlesMultipleCommands) {
2722 std::optional<H4PacketWithHci> host_packet;
2723 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2724 [&host_packet](H4PacketWithHci&& packet) {
2725 host_packet = std::move(packet);
2726 });
2727 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2728 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2729
2730 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2731 std::move(send_to_controller_fn),
2732 /*le_acl_credits_to_reserve=*/1,
2733 /*br_edr_acl_credits_to_reserve=*/0);
2734
2735 constexpr uint16_t kHandle = 123;
2736
2737 // Test that the proxy can parse a CFrame containing multiple commands and
2738 // pass it through. We pack 3 CONNECTION_REQ commands into one CFrame.
2739 constexpr size_t kNumCommands = 3;
2740 constexpr size_t kCmdLen = emboss::L2capConnectionReq::IntrinsicSizeInBytes();
2741 constexpr size_t kL2capLength =
2742 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + kCmdLen * kNumCommands;
2743 constexpr size_t kHciLength =
2744 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
2745 std::array<uint8_t, kHciLength> hci_arr{};
2746 H4PacketWithHci l2cap_cframe_packet{emboss::H4PacketType::ACL_DATA,
2747 pw::span(hci_arr.data(), kHciLength)};
2748
2749 // ACL header
2750 PW_TEST_ASSERT_OK_AND_ASSIGN(
2751 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
2752 acl.header().handle().Write(kHandle);
2753 acl.data_total_length().Write(kL2capLength);
2754 EXPECT_EQ(kL2capLength, acl.payload().BackingStorage().SizeInBytes());
2755
2756 // L2CAP header
2757 auto l2cap =
2758 emboss::MakeCFrameView(acl.payload().BackingStorage().data(),
2759 acl.payload().BackingStorage().SizeInBytes());
2760 l2cap.pdu_length().Write(kNumCommands * kCmdLen);
2761 l2cap.channel_id().Write(
2762 cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING));
2763 EXPECT_TRUE(l2cap.Ok());
2764
2765 auto command_buffer =
2766 pw::span(l2cap.payload().BackingStorage().data(),
2767 l2cap.payload().BackingStorage().SizeInBytes());
2768 EXPECT_EQ(l2cap.payload().BackingStorage().SizeInBytes(),
2769 kCmdLen * kNumCommands);
2770
2771 do {
2772 // CONNECTION_REQ
2773 auto cmd_writer = emboss::MakeL2capConnectionReqView(command_buffer.data(),
2774 command_buffer.size());
2775 cmd_writer.command_header().code().Write(
2776 emboss::L2capSignalingPacketCode::CONNECTION_REQ);
2777 // Note data_length doesn't include command header.
2778 cmd_writer.command_header().data_length().Write(
2779 kCmdLen - emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
2780 cmd_writer.psm().Write(1);
2781 cmd_writer.source_cid().Write(1);
2782 EXPECT_TRUE(cmd_writer.Ok());
2783 EXPECT_EQ(cmd_writer.SizeInBytes(), kCmdLen);
2784 command_buffer = command_buffer.subspan(cmd_writer.SizeInBytes());
2785 } while (!command_buffer.empty());
2786
2787 proxy.HandleH4HciFromController(std::move(l2cap_cframe_packet));
2788 // We should get back what we sent, since the proxy doesn't consume
2789 // CONNECTION_REQ commands. It would be nice to also verify the individual
2790 // commands were parsed out but hooks don't exist for that at the time of
2791 // writing.
2792 EXPECT_TRUE(host_packet.has_value());
2793 EXPECT_EQ(host_packet->GetHciSpan().size(), kHciLength);
2794 }
2795
TEST_F(AcluSignalingChannelTest,InvalidPacketForwarded)2796 TEST_F(AcluSignalingChannelTest, InvalidPacketForwarded) {
2797 std::optional<H4PacketWithHci> host_packet;
2798 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2799 [&host_packet](H4PacketWithHci&& packet) {
2800 host_packet = std::move(packet);
2801 });
2802 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2803 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2804
2805 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2806 std::move(send_to_controller_fn),
2807 /*le_acl_credits_to_reserve=*/1,
2808 /*br_edr_acl_credits_to_reserve=*/0);
2809
2810 constexpr uint16_t kHandle = 123;
2811
2812 // Test that the proxy forwards on invalid L2cap B-frames destined for
2813 // signaling channel.
2814
2815 constexpr size_t kL2capLength =
2816 emboss::BasicL2capHeader::IntrinsicSizeInBytes();
2817 constexpr size_t kHciLength =
2818 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
2819 std::array<uint8_t, kHciLength> hci_arr{};
2820 H4PacketWithHci l2cap_cframe_packet{emboss::H4PacketType::ACL_DATA,
2821 pw::span(hci_arr.data(), kHciLength)};
2822
2823 // ACL header
2824 PW_TEST_ASSERT_OK_AND_ASSIGN(
2825 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
2826 acl.header().handle().Write(kHandle);
2827 acl.data_total_length().Write(kL2capLength);
2828 EXPECT_EQ(kL2capLength, acl.payload().BackingStorage().SizeInBytes());
2829
2830 // L2CAP header
2831 auto l2cap =
2832 emboss::MakeCFrameView(acl.payload().BackingStorage().data(),
2833 acl.payload().BackingStorage().SizeInBytes());
2834 // Invalid length, since we aren't encoding a payload.
2835 l2cap.pdu_length().Write(1);
2836 l2cap.channel_id().Write(
2837 cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING));
2838 EXPECT_FALSE(l2cap.Ok());
2839
2840 proxy.HandleH4HciFromController(std::move(l2cap_cframe_packet));
2841 // We should get back what we sent.
2842 EXPECT_TRUE(host_packet.has_value());
2843 EXPECT_EQ(host_packet->GetHciSpan().size(), kHciLength);
2844 }
2845
2846 // ########## ProxyHostConnectionEventTest
2847
2848 class ProxyHostConnectionEventTest : public ProxyHostTest {};
2849
TEST_F(ProxyHostConnectionEventTest,ConnectionCompletePassthroughOk)2850 TEST_F(ProxyHostConnectionEventTest, ConnectionCompletePassthroughOk) {
2851 size_t host_called = 0;
2852 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2853 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2854
2855 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
2856 [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
2857 ++host_called;
2858 });
2859
2860 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2861 std::move(send_to_controller_fn),
2862 /*le_acl_credits_to_reserve=*/0,
2863 /*br_edr_acl_credits_to_reserve=*/0);
2864
2865 PW_TEST_EXPECT_OK(
2866 SendConnectionCompleteEvent(proxy, 1, emboss::StatusCode::SUCCESS));
2867 EXPECT_EQ(host_called, 1U);
2868
2869 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, 1));
2870 EXPECT_EQ(host_called, 2U);
2871 }
2872
TEST_F(ProxyHostConnectionEventTest,ConnectionCompleteWithErrorStatusPassthroughOk)2873 TEST_F(ProxyHostConnectionEventTest,
2874 ConnectionCompleteWithErrorStatusPassthroughOk) {
2875 size_t host_called = 0;
2876 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2877 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2878
2879 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
2880 [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
2881 ++host_called;
2882 });
2883
2884 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2885 std::move(send_to_controller_fn),
2886 /*le_acl_credits_to_reserve=*/0,
2887 /*br_edr_acl_credits_to_reserve=*/0);
2888
2889 PW_TEST_EXPECT_OK(SendConnectionCompleteEvent(
2890 proxy, 1, emboss::StatusCode::CONNECTION_FAILED_TO_BE_ESTABLISHED));
2891 EXPECT_EQ(host_called, 1U);
2892 }
2893
TEST_F(ProxyHostConnectionEventTest,LeConnectionCompletePassthroughOk)2894 TEST_F(ProxyHostConnectionEventTest, LeConnectionCompletePassthroughOk) {
2895 size_t host_called = 0;
2896 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2897 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2898
2899 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
2900 [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
2901 ++host_called;
2902 });
2903
2904 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2905 std::move(send_to_controller_fn),
2906 /*le_acl_credits_to_reserve=*/0,
2907 /*br_edr_acl_credits_to_reserve=*/0);
2908
2909 PW_TEST_EXPECT_OK(
2910 SendLeConnectionCompleteEvent(proxy, 1, emboss::StatusCode::SUCCESS));
2911 EXPECT_EQ(host_called, 1U);
2912 }
2913
2914 class L2capStatusTrackerTest : public ProxyHostTest,
2915 public L2capStatusDelegate {
2916 public:
2917 static constexpr uint16_t kPsm = 1;
2918
ShouldTrackPsm(uint16_t psm)2919 bool ShouldTrackPsm(uint16_t psm) override { return psm == kPsm; }
HandleConnectionComplete(const L2capChannelConnectionInfo & i)2920 void HandleConnectionComplete(const L2capChannelConnectionInfo& i) override {
2921 EXPECT_FALSE(info.has_value());
2922 PW_CHECK(proxy_ptr);
2923 info.emplace(i);
2924 // Test we can create channel directly in callback.
2925 l2cap_channel =
2926 BuildBasicL2capChannel(*proxy_ptr,
2927 {.handle = i.connection_handle,
2928 .local_cid = i.local_cid,
2929 .remote_cid = i.remote_cid,
2930 .transport = AclTransportType::kBrEdr});
2931 }
HandleDisconnectionComplete(const L2capChannelConnectionInfo & i)2932 void HandleDisconnectionComplete(
2933 const L2capChannelConnectionInfo& i) override {
2934 ASSERT_TRUE(info.has_value());
2935 EXPECT_EQ(info->direction, i.direction);
2936 EXPECT_EQ(info->connection_handle, i.connection_handle);
2937 EXPECT_EQ(info->remote_cid, i.remote_cid);
2938 EXPECT_EQ(info->local_cid, i.local_cid);
2939 info.reset();
2940 }
2941
2942 ProxyHost* proxy_ptr = nullptr;
2943 std::optional<L2capChannelConnectionInfo> info;
2944 std::optional<BasicL2capChannel> l2cap_channel;
2945 };
2946
TEST_F(L2capStatusTrackerTest,L2capEventsCalled)2947 TEST_F(L2capStatusTrackerTest, L2capEventsCalled) {
2948 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2949 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2950
2951 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
2952 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2953
2954 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2955 std::move(send_to_controller_fn),
2956 /*le_acl_credits_to_reserve=*/0,
2957 /*br_edr_acl_credits_to_reserve=*/0);
2958 proxy_ptr = &proxy;
2959
2960 constexpr uint16_t kSourceCid = 30;
2961 constexpr uint16_t kDestinationCid = 31;
2962 constexpr uint16_t kHandle = 123;
2963
2964 proxy.RegisterL2capStatusDelegate(*this);
2965
2966 PW_TEST_EXPECT_OK(
2967 SendConnectionCompleteEvent(proxy, kHandle, emboss::StatusCode::SUCCESS));
2968
2969 // First send CONNECTION_REQ to setup partial connection
2970 PW_TEST_EXPECT_OK(SendL2capConnectionReq(proxy, kHandle, kSourceCid, kPsm));
2971 EXPECT_FALSE(info.has_value());
2972
2973 // Send non-successful connection response.
2974 PW_TEST_EXPECT_OK(SendL2capConnectionRsp(
2975 proxy,
2976 kHandle,
2977 kSourceCid,
2978 kDestinationCid,
2979 emboss::L2capConnectionRspResultCode::INVALID_SOURCE_CID));
2980 EXPECT_FALSE(info.has_value());
2981
2982 // Send successful connection response, but expect that it will not have
2983 // called listener since the connection was closed with error already.
2984 PW_TEST_EXPECT_OK(
2985 SendL2capConnectionRsp(proxy,
2986 kHandle,
2987 kSourceCid,
2988 kDestinationCid,
2989 emboss::L2capConnectionRspResultCode::SUCCESSFUL));
2990 EXPECT_FALSE(info.has_value());
2991
2992 // Send new connection req
2993 PW_TEST_EXPECT_OK(SendL2capConnectionReq(proxy, kHandle, kSourceCid, kPsm));
2994 EXPECT_FALSE(info.has_value());
2995
2996 // Send rsp with PENDING set.
2997 PW_TEST_EXPECT_OK(
2998 SendL2capConnectionRsp(proxy,
2999 kHandle,
3000 kSourceCid,
3001 kDestinationCid,
3002 emboss::L2capConnectionRspResultCode::PENDING));
3003 EXPECT_FALSE(info.has_value());
3004
3005 // Send success rsp
3006 PW_TEST_EXPECT_OK(
3007 SendL2capConnectionRsp(proxy,
3008 kHandle,
3009 kSourceCid,
3010 kDestinationCid,
3011 emboss::L2capConnectionRspResultCode::SUCCESSFUL));
3012 EXPECT_TRUE(info.has_value());
3013 EXPECT_EQ(info->local_cid, kDestinationCid);
3014
3015 // Send disconnect
3016 PW_TEST_EXPECT_OK(SendL2capDisconnectRsp(
3017 proxy, AclTransportType::kBrEdr, kHandle, kSourceCid, kDestinationCid));
3018 EXPECT_FALSE(info.has_value());
3019
3020 proxy.UnregisterL2capStatusDelegate(*this);
3021
3022 // Send successful connection sequence with no listeners.
3023 PW_TEST_EXPECT_OK(SendL2capConnectionReq(proxy, kHandle, kSourceCid, kPsm));
3024 PW_TEST_EXPECT_OK(
3025 SendL2capConnectionRsp(proxy,
3026 kHandle,
3027 kSourceCid,
3028 kDestinationCid,
3029 emboss::L2capConnectionRspResultCode::SUCCESSFUL));
3030 EXPECT_FALSE(info.has_value());
3031 }
3032
TEST_F(ProxyHostConnectionEventTest,HciDisconnectionAlertsListeners)3033 TEST_F(ProxyHostConnectionEventTest, HciDisconnectionAlertsListeners) {
3034 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
3035 [](H4PacketWithH4&&) {});
3036 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
3037 [](H4PacketWithHci&&) {});
3038 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3039 std::move(send_to_controller_fn),
3040 /*le_acl_credits_to_reserve=*/0,
3041 /*br_edr_acl_credits_to_reserve=*/0);
3042
3043 constexpr uint16_t kPsm = 1;
3044
3045 class TestStatusDelegate final : public L2capStatusDelegate {
3046 public:
3047 bool ShouldTrackPsm(uint16_t psm) override { return psm == kPsm; }
3048 void HandleConnectionComplete(const L2capChannelConnectionInfo&) override {
3049 ++connections_received;
3050 }
3051 void HandleDisconnectionComplete(
3052 const L2capChannelConnectionInfo&) override {
3053 ++disconnections_received;
3054 }
3055
3056 int connections_received = 0;
3057 int disconnections_received = 0;
3058 };
3059
3060 TestStatusDelegate test_delegate;
3061 proxy.RegisterL2capStatusDelegate(test_delegate);
3062
3063 constexpr uint16_t Handle1 = 0x123, Handle2 = 0x124;
3064 PW_TEST_EXPECT_OK(
3065 SendConnectionCompleteEvent(proxy, Handle1, emboss::StatusCode::SUCCESS));
3066 PW_TEST_EXPECT_OK(
3067 SendConnectionCompleteEvent(proxy, Handle2, emboss::StatusCode::SUCCESS));
3068
3069 // Establish three connected_channels:
3070 // handle = 0x123, PSM = 1 | handle = 0x124, PSM = 1 | handle = 0x123, PSM =
3071 // 1
3072 constexpr uint16_t kStartSourceCid = 0x111;
3073 constexpr uint16_t kStartDestinationCid = 0x211;
3074 for (size_t i = 0; i < 3; ++i) {
3075 PW_TEST_EXPECT_OK(SendL2capConnectionReq(
3076 proxy, i == 1 ? Handle2 : Handle1, kStartSourceCid + i, kPsm));
3077 PW_TEST_EXPECT_OK(SendL2capConnectionRsp(
3078 proxy,
3079 i == 1 ? Handle2 : Handle1,
3080 kStartSourceCid + i,
3081 kStartDestinationCid + i,
3082 emboss::L2capConnectionRspResultCode::SUCCESSFUL));
3083 }
3084
3085 EXPECT_EQ(test_delegate.connections_received, 3);
3086 EXPECT_EQ(test_delegate.disconnections_received, 0);
3087
3088 // Disconnect handle1, which should disconnect first and third channel.
3089 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, Handle1));
3090 EXPECT_EQ(test_delegate.disconnections_received, 2);
3091
3092 // Confirm remaining channel can still be disconnected properly.
3093 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, Handle2));
3094 EXPECT_EQ(test_delegate.disconnections_received, 3);
3095
3096 proxy.UnregisterL2capStatusDelegate(test_delegate);
3097 }
3098
TEST_F(ProxyHostConnectionEventTest,HciDisconnectionFromControllerClosesChannels)3099 TEST_F(ProxyHostConnectionEventTest,
3100 HciDisconnectionFromControllerClosesChannels) {
3101 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
3102 [](H4PacketWithH4&&) {});
3103 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
3104 [](H4PacketWithHci&&) {});
3105 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3106 std::move(send_to_controller_fn),
3107 /*le_acl_credits_to_reserve=*/0,
3108 /*br_edr_acl_credits_to_reserve=*/0);
3109
3110 constexpr uint16_t kHandle = 0x123;
3111 constexpr uint16_t kStartingCid = 0x111;
3112 int events_received = 0;
3113 auto event_fn = [&events_received](L2capChannelEvent event) {
3114 ++events_received;
3115 EXPECT_EQ(event, L2capChannelEvent::kChannelClosedByOther);
3116 };
3117 BasicL2capChannel chan1 = BuildBasicL2capChannel(proxy,
3118 {.handle = kHandle,
3119 .local_cid = kStartingCid,
3120 .remote_cid = kStartingCid,
3121 .event_fn = event_fn});
3122 // chan2 is on a different connection so should not be closed
3123 BasicL2capChannel chan2 =
3124 BuildBasicL2capChannel(proxy,
3125 {.handle = kHandle + 1,
3126 .local_cid = kStartingCid + 1,
3127 .remote_cid = kStartingCid + 1,
3128 .event_fn = event_fn});
3129 BasicL2capChannel chan3 =
3130 BuildBasicL2capChannel(proxy,
3131 {.handle = kHandle,
3132 .local_cid = kStartingCid + 2,
3133 .remote_cid = kStartingCid + 2,
3134 .event_fn = event_fn});
3135
3136 EXPECT_EQ(chan1.state(), L2capChannel::State::kRunning);
3137 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3138 EXPECT_EQ(chan3.state(), L2capChannel::State::kRunning);
3139
3140 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, kHandle));
3141
3142 EXPECT_EQ(events_received, 2);
3143 EXPECT_EQ(chan1.state(), L2capChannel::State::kClosed);
3144 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3145 EXPECT_EQ(chan3.state(), L2capChannel::State::kClosed);
3146
3147 // Confirm L2CAP_DISCONNECTION_RSP packet does not result in another event.
3148 PW_TEST_EXPECT_OK(SendL2capDisconnectRsp(
3149 proxy, AclTransportType::kLe, kHandle, kStartingCid, kStartingCid));
3150 EXPECT_EQ(events_received, 2);
3151 }
3152
TEST_F(ProxyHostConnectionEventTest,L2capDisconnectionRspFromHostClosesChannels)3153 TEST_F(ProxyHostConnectionEventTest,
3154 L2capDisconnectionRspFromHostClosesChannels) {
3155 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
3156 [](H4PacketWithH4&&) {});
3157 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
3158 [](H4PacketWithHci&&) {});
3159 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3160 std::move(send_to_controller_fn),
3161 /*le_acl_credits_to_reserve=*/0,
3162 /*br_edr_acl_credits_to_reserve=*/0);
3163
3164 constexpr uint16_t kHandle = 0x123;
3165 constexpr uint16_t kStartingSourceCid = 0x111;
3166 constexpr uint16_t kStartingDestinationCid = 0x211;
3167 int events_received = 0;
3168 auto event_fn = [&events_received](L2capChannelEvent event) {
3169 ++events_received;
3170 EXPECT_EQ(event, L2capChannelEvent::kChannelClosedByOther);
3171 };
3172 BasicL2capChannel chan1 =
3173 BuildBasicL2capChannel(proxy,
3174 {.handle = kHandle,
3175 .local_cid = kStartingDestinationCid,
3176 .remote_cid = kStartingSourceCid,
3177 .event_fn = event_fn});
3178 BasicL2capChannel chan2 =
3179 BuildBasicL2capChannel(proxy,
3180 {.handle = kHandle,
3181 .local_cid = kStartingDestinationCid + 1,
3182 .remote_cid = kStartingSourceCid + 1,
3183 .event_fn = event_fn});
3184 BasicL2capChannel chan3 =
3185 BuildBasicL2capChannel(proxy,
3186 {.handle = kHandle,
3187 .local_cid = kStartingDestinationCid + 2,
3188 .remote_cid = kStartingSourceCid + 2,
3189 .event_fn = event_fn});
3190
3191 EXPECT_EQ(chan1.state(), L2capChannel::State::kRunning);
3192 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3193 EXPECT_EQ(chan3.state(), L2capChannel::State::kRunning);
3194
3195 // Close chan1's & chan2's underlying L2CAP connections.
3196 PW_TEST_EXPECT_OK(
3197 SendL2capDisconnectRsp(proxy,
3198 AclTransportType::kLe,
3199 kHandle,
3200 /*source_cid=*/kStartingSourceCid,
3201 /*destination_cid=*/kStartingDestinationCid));
3202 PW_TEST_EXPECT_OK(
3203 SendL2capDisconnectRsp(proxy,
3204 AclTransportType::kLe,
3205 kHandle,
3206 /*source_cid=*/kStartingSourceCid + 2,
3207 /*destination_cid=*/kStartingDestinationCid + 2));
3208
3209 EXPECT_EQ(events_received, 2);
3210 EXPECT_EQ(chan1.state(), L2capChannel::State::kClosed);
3211 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3212 EXPECT_EQ(chan3.state(), L2capChannel::State::kClosed);
3213
3214 // Confirm HCI disconnection only closes remaining channel.
3215 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, kHandle));
3216 EXPECT_EQ(chan2.state(), L2capChannel::State::kClosed);
3217 EXPECT_EQ(events_received, 3);
3218 }
3219
TEST_F(ProxyHostConnectionEventTest,HciDisconnectionFromHostClosesChannels)3220 TEST_F(ProxyHostConnectionEventTest, HciDisconnectionFromHostClosesChannels) {
3221 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
3222 [](H4PacketWithH4&&) {});
3223 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
3224 [](H4PacketWithHci&&) {});
3225 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3226 std::move(send_to_controller_fn),
3227 /*le_acl_credits_to_reserve=*/0,
3228 /*br_edr_acl_credits_to_reserve=*/0);
3229
3230 constexpr uint16_t kHandle = 0x123;
3231 constexpr uint16_t kStartingCid = 0x111;
3232 int events_received = 0;
3233 auto event_fn = [&events_received](L2capChannelEvent event) {
3234 ++events_received;
3235 EXPECT_EQ(event, L2capChannelEvent::kChannelClosedByOther);
3236 };
3237 BasicL2capChannel chan1 = BuildBasicL2capChannel(proxy,
3238 {.handle = kHandle,
3239 .local_cid = kStartingCid,
3240 .remote_cid = kStartingCid,
3241 .event_fn = event_fn});
3242 BasicL2capChannel chan2 =
3243 BuildBasicL2capChannel(proxy,
3244 {.handle = kHandle + 1,
3245 .local_cid = kStartingCid + 1,
3246 .remote_cid = kStartingCid + 1,
3247 .event_fn = event_fn});
3248 BasicL2capChannel chan3 =
3249 BuildBasicL2capChannel(proxy,
3250 {.handle = kHandle,
3251 .local_cid = kStartingCid + 2,
3252 .remote_cid = kStartingCid + 2,
3253 .event_fn = event_fn});
3254
3255 EXPECT_EQ(chan1.state(), L2capChannel::State::kRunning);
3256 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3257 EXPECT_EQ(chan3.state(), L2capChannel::State::kRunning);
3258
3259 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(
3260 proxy, kHandle, /*direction=*/Direction::kFromHost));
3261
3262 EXPECT_EQ(chan1.state(), L2capChannel::State::kClosed);
3263 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3264 EXPECT_EQ(chan3.state(), L2capChannel::State::kClosed);
3265 EXPECT_EQ(events_received, 2);
3266 }
3267
TEST_F(ProxyHostConnectionEventTest,L2capDisconnectionRspFromControllerClosesChannels)3268 TEST_F(ProxyHostConnectionEventTest,
3269 L2capDisconnectionRspFromControllerClosesChannels) {
3270 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
3271 [](H4PacketWithH4&&) {});
3272 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
3273 [](H4PacketWithHci&&) {});
3274 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3275 std::move(send_to_controller_fn),
3276 /*le_acl_credits_to_reserve=*/0,
3277 /*br_edr_acl_credits_to_reserve=*/0);
3278
3279 constexpr uint16_t kHandle = 0x123;
3280 constexpr uint16_t kStartingCid = 0x111;
3281 int events_received = 0;
3282 auto event_fn = [&events_received](L2capChannelEvent event) {
3283 ++events_received;
3284 EXPECT_EQ(event, L2capChannelEvent::kChannelClosedByOther);
3285 };
3286 BasicL2capChannel chan1 = BuildBasicL2capChannel(proxy,
3287 {.handle = kHandle,
3288 .local_cid = kStartingCid,
3289 .remote_cid = kStartingCid,
3290 .event_fn = event_fn});
3291 BasicL2capChannel chan2 =
3292 BuildBasicL2capChannel(proxy,
3293 {.handle = kHandle,
3294 .local_cid = kStartingCid + 1,
3295 .remote_cid = kStartingCid + 1,
3296 .event_fn = event_fn});
3297 BasicL2capChannel chan3 =
3298 BuildBasicL2capChannel(proxy,
3299 {.handle = kHandle,
3300 .local_cid = kStartingCid + 2,
3301 .remote_cid = kStartingCid + 2,
3302 .event_fn = event_fn});
3303
3304 EXPECT_EQ(chan1.state(), L2capChannel::State::kRunning);
3305 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3306 EXPECT_EQ(chan3.state(), L2capChannel::State::kRunning);
3307
3308 // Close chan1's & chan2's underlying L2CAP connections.
3309 PW_TEST_EXPECT_OK(
3310 SendL2capDisconnectRsp(proxy,
3311 AclTransportType::kLe,
3312 kHandle,
3313 kStartingCid,
3314 kStartingCid,
3315 /*direction=*/Direction::kFromController));
3316 PW_TEST_EXPECT_OK(
3317 SendL2capDisconnectRsp(proxy,
3318 AclTransportType::kLe,
3319 kHandle,
3320 kStartingCid + 2,
3321 kStartingCid + 2,
3322 /*direction=*/Direction::kFromController));
3323
3324 EXPECT_EQ(events_received, 2);
3325 EXPECT_EQ(chan1.state(), L2capChannel::State::kClosed);
3326 EXPECT_EQ(chan2.state(), L2capChannel::State::kRunning);
3327 EXPECT_EQ(chan3.state(), L2capChannel::State::kClosed);
3328
3329 // Confirm HCI disconnection only closes remaining channel.
3330 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, kHandle));
3331 EXPECT_EQ(chan2.state(), L2capChannel::State::kClosed);
3332 EXPECT_EQ(events_received, 3);
3333 }
3334
3335 // ########## AclFragTest
3336
3337 class AclFragTest : public ProxyHostTest {
3338 protected:
3339 static constexpr uint16_t kHandle = 0x4AD;
3340 static constexpr uint16_t kLocalCid = 0xC1D;
3341
3342 int packets_sent_to_host = 0;
3343 int packets_sent_to_controller = 0;
3344
GetProxy()3345 ProxyHost GetProxy() {
3346 // We can't add a ProxyHost member because it makes the test fixture too
3347 // large, so we provide a helper function instead.
3348 return ProxyHost(pw::bind_member<&AclFragTest::SendToHost>(this),
3349 pw::bind_member<&AclFragTest::SendToController>(this),
3350 /*le_acl_credits_to_reserve=*/0,
3351 /*br_edr_acl_credits_to_reserve=*/0);
3352 }
3353
3354 std::vector<multibuf::MultiBuf> payloads_from_controller;
3355
GetL2capChannel(ProxyHost & proxy)3356 BasicL2capChannel GetL2capChannel(ProxyHost& proxy) {
3357 return BuildBasicL2capChannel(
3358 proxy,
3359 BasicL2capParameters{
3360 .handle = kHandle,
3361 .local_cid = kLocalCid,
3362 .remote_cid = 0x123,
3363 .transport = AclTransportType::kLe,
3364 .payload_from_controller_fn =
3365 [this](multibuf::MultiBuf&& buffer) {
3366 payloads_from_controller.emplace_back(std::move(buffer));
3367 return std::nullopt; // Consume
3368 },
3369 });
3370 }
3371
ExpectPayloadsFromController(std::initializer_list<ConstByteSpan> expected_payloads)3372 void ExpectPayloadsFromController(
3373 std::initializer_list<ConstByteSpan> expected_payloads) {
3374 EXPECT_EQ(payloads_from_controller.size(), expected_payloads.size());
3375 if (payloads_from_controller.size() != expected_payloads.size()) {
3376 return;
3377 }
3378
3379 auto payloads_iter = payloads_from_controller.begin();
3380 for (ConstByteSpan expected : expected_payloads) {
3381 std::optional<pw::ByteSpan> payload = (payloads_iter++)->ContiguousSpan();
3382 PW_CHECK(payload.has_value());
3383 EXPECT_TRUE(std::equal(
3384 payload->begin(), payload->end(), expected.begin(), expected.end()));
3385 }
3386 }
3387
VerifyNormalOperationAfterRecombination(ProxyHost & proxy)3388 void VerifyNormalOperationAfterRecombination(ProxyHost& proxy) {
3389 // Verify things work normally after recombination ends.
3390 static constexpr std::array<uint8_t, 4> kPayload = {'D', 'o', 'n', 'e'};
3391 payloads_from_controller.clear();
3392 SendL2capBFrame(proxy, kHandle, kPayload, kPayload.size(), kLocalCid);
3393 ExpectPayloadsFromController({
3394 as_bytes(span(kPayload)),
3395 });
3396 }
3397
3398 private:
SendToHost(H4PacketWithHci &&)3399 void SendToHost(H4PacketWithHci&& /*packet*/) { ++packets_sent_to_host; }
3400
SendToController(H4PacketWithH4 &&)3401 void SendToController(H4PacketWithH4&& /*packet*/) {
3402 ++packets_sent_to_controller;
3403 }
3404 };
3405
TEST_F(AclFragTest,AclBiggerThanL2capDropped)3406 TEST_F(AclFragTest, AclBiggerThanL2capDropped) {
3407 ProxyHost proxy = GetProxy();
3408 BasicL2capChannel channel = GetL2capChannel(proxy);
3409
3410 // Send an ACL packet with more data than L2CAP header indicates.
3411 static constexpr std::array<uint8_t, 4> kPayload{};
3412 SendL2capBFrame(proxy, kHandle, kPayload, 1, kLocalCid);
3413
3414 // Should be dropped.
3415 EXPECT_EQ(packets_sent_to_host, 0);
3416 ExpectPayloadsFromController({});
3417 }
3418
TEST_F(AclFragTest,RecombinationWorksWithEmptyFirstPayload)3419 TEST_F(AclFragTest, RecombinationWorksWithEmptyFirstPayload) {
3420 ProxyHost proxy = GetProxy();
3421 BasicL2capChannel channel = GetL2capChannel(proxy);
3422
3423 static constexpr std::array<uint8_t, 4> kPayload = {0xA1, 0xB2, 0xC3, 0xD2};
3424
3425 // Fragment 1: ACL Header + L2CAP B-Frame Header + (no payload)
3426 PW_LOG_INFO("Sending frag 1: ACL + L2CAP header");
3427 SendL2capBFrame(proxy, kHandle, {}, kPayload.size(), kLocalCid);
3428
3429 // Fragment 2: ACL Header + Payload frag 2
3430 PW_LOG_INFO("Sending frag 2: ACL(CONT) + payload2");
3431 SendAclContinuingFrag(proxy, kHandle, kPayload);
3432
3433 EXPECT_EQ(packets_sent_to_host, 0);
3434 ExpectPayloadsFromController({
3435 as_bytes(span(kPayload)),
3436 });
3437
3438 VerifyNormalOperationAfterRecombination(proxy);
3439 }
3440
TEST_F(AclFragTest,RecombinationWorksWithSplitPayloads)3441 TEST_F(AclFragTest, RecombinationWorksWithSplitPayloads) {
3442 ProxyHost proxy = GetProxy();
3443 BasicL2capChannel channel = GetL2capChannel(proxy);
3444
3445 static constexpr std::array<uint8_t, 2> kPayloadFrag1 = {0xA1, 0xB2};
3446 static constexpr std::array<uint8_t, 2> kPayloadFrag2 = {0xC3, 0xD2};
3447 static constexpr std::array<uint8_t, 4> kPayload = {0xA1, 0xB2, 0xC3, 0xD2};
3448
3449 constexpr int kNumIter = 4;
3450
3451 for (int i = 0; i < kNumIter; ++i) {
3452 // Fragment 1: ACL Header + L2CAP B-Frame Header + Payload frag 1
3453 PW_LOG_INFO("Sending frag 1: ACL + L2CAP header + payload1");
3454 SendL2capBFrame(proxy, kHandle, kPayloadFrag1, kPayload.size(), kLocalCid);
3455
3456 // Fragment 2: ACL Header + Payload frag 2
3457 PW_LOG_INFO("Sending frag 2: ACL(CONT) + payload2");
3458 SendAclContinuingFrag(proxy, kHandle, kPayloadFrag2);
3459 }
3460
3461 EXPECT_EQ(packets_sent_to_host, 0);
3462 ExpectPayloadsFromController({
3463 as_bytes(span(kPayload)),
3464 as_bytes(span(kPayload)),
3465 as_bytes(span(kPayload)),
3466 as_bytes(span(kPayload)),
3467 });
3468
3469 VerifyNormalOperationAfterRecombination(proxy);
3470 }
3471
TEST_F(AclFragTest,UnexpectedContinuingFragment)3472 TEST_F(AclFragTest, UnexpectedContinuingFragment) {
3473 ProxyHost proxy = GetProxy();
3474 BasicL2capChannel channel = GetL2capChannel(proxy);
3475
3476 static constexpr std::array<uint8_t, 4> kPayload = {0xA1, 0xB2, 0xC3, 0xD2};
3477
3478 // Send an unexpected CONTINUING_FRAGMENT
3479 PW_LOG_INFO("Sending frag 1: ACL(CONT) + payload");
3480 SendAclContinuingFrag(proxy, kHandle, kPayload);
3481
3482 ExpectPayloadsFromController({});
3483 EXPECT_EQ(packets_sent_to_host, 1); // Should be passed on to host
3484
3485 VerifyNormalOperationAfterRecombination(proxy);
3486 }
3487
TEST_F(AclFragTest,UnexpectedFirstFragment)3488 TEST_F(AclFragTest, UnexpectedFirstFragment) {
3489 ProxyHost proxy = GetProxy();
3490 BasicL2capChannel channel = GetL2capChannel(proxy);
3491
3492 static constexpr std::array<uint8_t, 2> kPayloadFrag1 = {0xA1, 0xB2};
3493 static constexpr std::array<uint8_t, 2> kPayloadFrag2 = {0xC3, 0xD2};
3494 static constexpr std::array<uint8_t, 4> kPayload = {0xA1, 0xB2, 0xC3, 0xD2};
3495
3496 // PDU A: Fragment 1: Start recombination by sending first fragment.
3497 PW_LOG_INFO("Sending frag 1: ACL + L2CAP header + payload1");
3498 SendL2capBFrame(proxy, kHandle, {}, 100, kLocalCid);
3499
3500 // We never send the 100 byte payload here.
3501
3502 // So this new first-fragment is unexpected:
3503 // PDU B: Fragment 1: ACL Header + L2CAP B-Frame Header + Payload frag 1
3504 PW_LOG_INFO("Sending frag 1: ACL + L2CAP header + payload1");
3505 SendL2capBFrame(proxy, kHandle, kPayloadFrag1, kPayload.size(), kLocalCid);
3506
3507 // PDU B: Fragment 2: ACL Header + Payload frag 2
3508 PW_LOG_INFO("Sending frag 2: ACL(CONT) + payload2");
3509 SendAclContinuingFrag(proxy, kHandle, kPayloadFrag2);
3510
3511 // Nothing should be sent to the host. The first fragment of PDU A is dropped.
3512 EXPECT_EQ(packets_sent_to_host, 0);
3513
3514 // PDU B is delivered.
3515 ExpectPayloadsFromController({
3516 as_bytes(span(kPayload)),
3517 });
3518
3519 VerifyNormalOperationAfterRecombination(proxy);
3520 }
3521
TEST_F(AclFragTest,ContinuingFragmentTooLarge)3522 TEST_F(AclFragTest, ContinuingFragmentTooLarge) {
3523 ProxyHost proxy = GetProxy();
3524 BasicL2capChannel channel = GetL2capChannel(proxy);
3525
3526 static constexpr std::array<uint8_t, 2> kPayloadFrag1 = {0xA1, 0xB2};
3527 static constexpr std::array<uint8_t, 5> kPayloadFrag2TooBig = {
3528 0xC3, 0xD2, 0xBA, 0xAA, 0xAD};
3529 static constexpr std::array<uint8_t, 4> kPayload = {0xA1, 0xB2, 0xC3, 0xD2};
3530
3531 // Fragment 1: ACL Header + L2CAP B-Frame Header + Payload frag 1
3532 PW_LOG_INFO("Sending frag 1: ACL + L2CAP header + payload1");
3533 SendL2capBFrame(proxy, kHandle, kPayloadFrag1, kPayload.size(), kLocalCid);
3534
3535 // Fragment 2: ACL Header + Payload frag 2
3536 PW_LOG_INFO("Sending frag 2: ACL(CONT) + payload2 (too big)");
3537 SendAclContinuingFrag(proxy, kHandle, kPayloadFrag2TooBig);
3538
3539 ExpectPayloadsFromController({});
3540
3541 // This was for a channel owned by the proxy so it should have been dropped.
3542 EXPECT_EQ(packets_sent_to_host, 0);
3543
3544 VerifyNormalOperationAfterRecombination(proxy);
3545 }
3546
TEST_F(AclFragTest,CanReceiveUnfragmentedPduOnOneChannelWhileRecombiningOnAnother)3547 TEST_F(AclFragTest,
3548 CanReceiveUnfragmentedPduOnOneChannelWhileRecombiningOnAnother) {
3549 ProxyHost proxy = GetProxy();
3550
3551 // Channel 1
3552 static constexpr std::array<uint8_t, 2> kPayload1Frag1 = {0xA1, 0xB2};
3553 static constexpr std::array<uint8_t, 2> kPayload1Frag2 = {0xC3, 0xD2};
3554 static constexpr std::array<uint8_t, 4> kPayload1 = {0xA1, 0xB2, 0xC3, 0xD2};
3555
3556 int channel1_sends_called = 0;
3557 BasicL2capChannel channel = BuildBasicL2capChannel(
3558 proxy,
3559 BasicL2capParameters{
3560 .handle = kHandle,
3561 .local_cid = kLocalCid,
3562 .remote_cid = 0x123,
3563 .transport = AclTransportType::kLe,
3564 .payload_from_controller_fn =
3565 [&channel1_sends_called](multibuf::MultiBuf&& buffer) {
3566 ++channel1_sends_called;
3567 std::optional<pw::ByteSpan> payload = buffer.ContiguousSpan();
3568 ConstByteSpan expected_bytes = as_bytes(span(kPayload1));
3569 EXPECT_TRUE(payload.has_value());
3570 EXPECT_TRUE(std::equal(payload->begin(),
3571 payload->end(),
3572 expected_bytes.begin(),
3573 expected_bytes.end()));
3574 return std::nullopt;
3575 },
3576 });
3577
3578 // Channel 2
3579 static constexpr uint16_t kHandle2 = 0x4D2;
3580 static constexpr uint16_t kLocalCid2 = 0xC2D;
3581 static constexpr std::array<uint8_t, 4> kPayload2 = {0x33, 0x66, 0x99, 0xCC};
3582
3583 int channel2_sends_called = 0;
3584 BasicL2capChannel channel2 = BuildBasicL2capChannel(
3585 proxy,
3586 BasicL2capParameters{
3587 .handle = kHandle2,
3588 .local_cid = kLocalCid2,
3589 .remote_cid = 0x321,
3590 .transport = AclTransportType::kLe,
3591 .payload_from_controller_fn =
3592 [&channel2_sends_called](multibuf::MultiBuf&& buffer) {
3593 ++channel2_sends_called;
3594 std::optional<pw::ByteSpan> payload = buffer.ContiguousSpan();
3595 ConstByteSpan expected_bytes = as_bytes(span(kPayload2));
3596 EXPECT_TRUE(payload.has_value());
3597 EXPECT_TRUE(std::equal(payload->begin(),
3598 payload->end(),
3599 expected_bytes.begin(),
3600 expected_bytes.end()));
3601 return std::nullopt;
3602 },
3603 });
3604
3605 // Channel 1: Fragment 1: ACL Header + L2CAP B-Frame Header + Payload frag 1
3606 PW_LOG_INFO("Sending frag 1: ACL + L2CAP header + payload1");
3607 SendL2capBFrame(proxy, kHandle, kPayload1Frag1, kPayload1.size(), kLocalCid);
3608
3609 // Channel 2: Send full PDU
3610 SendL2capBFrame(proxy, kHandle2, kPayload2, kPayload2.size(), kLocalCid2);
3611 EXPECT_EQ(channel2_sends_called, 1);
3612
3613 // Channel 1: Fragment 2: ACL Header + Payload frag 2
3614 PW_LOG_INFO("Sending frag 2: ACL(CONT) + payload2");
3615 SendAclContinuingFrag(proxy, kHandle, kPayload1Frag2);
3616
3617 EXPECT_EQ(channel1_sends_called, 1);
3618 EXPECT_EQ(packets_sent_to_host, 0);
3619 }
3620
3621 } // namespace
3622 } // namespace pw::bluetooth::proxy
3623