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 #include <numeric>
19
20 #include "pw_bluetooth/att.emb.h"
21 #include "pw_bluetooth/emboss_util.h"
22 #include "pw_bluetooth/hci_commands.emb.h"
23 #include "pw_bluetooth/hci_common.emb.h"
24 #include "pw_bluetooth/hci_data.emb.h"
25 #include "pw_bluetooth/hci_events.emb.h"
26 #include "pw_bluetooth/hci_h4.emb.h"
27 #include "pw_bluetooth/l2cap_frames.emb.h"
28 #include "pw_bluetooth/rfcomm_frames.emb.h"
29 #include "pw_bluetooth_proxy/h4_packet.h"
30 #include "pw_bluetooth_proxy/internal/logical_transport.h"
31 #include "pw_containers/flat_map.h"
32 #include "pw_function/function.h"
33 #include "pw_status/status.h"
34 #include "pw_status/try.h"
35 #include "pw_unit_test/framework.h" // IWYU pragma: keep
36
37 namespace pw::bluetooth::proxy {
38
39 namespace {
40
41 using containers::FlatMap;
42
43 // TODO: https://pwbug.dev/349700888 - Once size is configurable, switch to
44 // specifying directly. Until then this should match
45 // AclConnection::kMaxConnections.
46 static constexpr size_t kMaxProxyActiveConnections = 10;
47
48 // ########## Util functions
49
50 // Populate passed H4 command buffer and return Emboss view on it.
51 template <typename EmbossT>
CreateAndPopulateToControllerView(H4PacketWithH4 & h4_packet,emboss::OpCode opcode)52 Result<EmbossT> CreateAndPopulateToControllerView(H4PacketWithH4& h4_packet,
53 emboss::OpCode opcode) {
54 std::iota(h4_packet.GetHciSpan().begin(), h4_packet.GetHciSpan().end(), 100);
55 h4_packet.SetH4Type(emboss::H4PacketType::COMMAND);
56 PW_TRY_ASSIGN(auto view, MakeEmbossWriter<EmbossT>(h4_packet.GetHciSpan()));
57 view.header().opcode_enum().Write(opcode);
58 return view;
59 }
60
61 // Return a populated H4 command buffer of a type that proxy host doesn't
62 // interact with.
PopulateNoninteractingToControllerBuffer(H4PacketWithH4 & h4_packet)63 Status PopulateNoninteractingToControllerBuffer(H4PacketWithH4& h4_packet) {
64 return CreateAndPopulateToControllerView<emboss::InquiryCommandWriter>(
65 h4_packet, emboss::OpCode::LINK_KEY_REQUEST_REPLY)
66 .status();
67 }
68
69 // Populate passed H4 event buffer and return Emboss view on it.
70 template <typename EmbossT>
CreateAndPopulateToHostEventView(H4PacketWithHci & h4_packet,emboss::EventCode event_code)71 Result<EmbossT> CreateAndPopulateToHostEventView(H4PacketWithHci& h4_packet,
72 emboss::EventCode event_code) {
73 std::iota(h4_packet.GetHciSpan().begin(), h4_packet.GetHciSpan().end(), 0x10);
74 h4_packet.SetH4Type(emboss::H4PacketType::EVENT);
75
76 PW_TRY_ASSIGN(auto view, MakeEmbossWriter<EmbossT>(h4_packet.GetHciSpan()));
77 view.header().event_code_enum().Write(event_code);
78 view.status().Write(emboss::StatusCode::SUCCESS);
79 EXPECT_TRUE(view.Ok());
80 return view;
81 }
82
83 // Send an LE_Read_Buffer_Size (V2) CommandComplete event to `proxy` to request
84 // the reservation of a number of LE ACL send credits.
SendLeReadBufferResponseFromController(ProxyHost & proxy,uint8_t num_credits_to_reserve)85 Status SendLeReadBufferResponseFromController(ProxyHost& proxy,
86 uint8_t num_credits_to_reserve) {
87 std::array<
88 uint8_t,
89 emboss::LEReadBufferSizeV2CommandCompleteEventWriter::SizeInBytes()>
90 hci_arr;
91 hci_arr.fill(0);
92 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
93 PW_TRY_ASSIGN(auto view,
94 CreateAndPopulateToHostEventView<
95 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
96 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
97 view.command_complete().command_opcode_enum().Write(
98 emboss::OpCode::LE_READ_BUFFER_SIZE_V2);
99 view.total_num_le_acl_data_packets().Write(num_credits_to_reserve);
100
101 proxy.HandleH4HciFromController(std::move(h4_packet));
102 return OkStatus();
103 }
104
SendReadBufferResponseFromController(ProxyHost & proxy,uint8_t num_credits_to_reserve)105 Status SendReadBufferResponseFromController(ProxyHost& proxy,
106 uint8_t num_credits_to_reserve) {
107 std::array<uint8_t,
108 emboss::ReadBufferSizeCommandCompleteEventWriter::SizeInBytes()>
109 hci_arr{};
110 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
111 PW_TRY_ASSIGN(auto view,
112 CreateAndPopulateToHostEventView<
113 emboss::ReadBufferSizeCommandCompleteEventWriter>(
114 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
115 view.command_complete().command_opcode_enum().Write(
116 emboss::OpCode::READ_BUFFER_SIZE);
117 view.total_num_acl_data_packets().Write(num_credits_to_reserve);
118 EXPECT_TRUE(view.Ok());
119
120 proxy.HandleH4HciFromController(std::move(h4_packet));
121 return OkStatus();
122 }
123
124 // Send a Number_of_Completed_Packets event to `proxy` that reports each
125 // {connection handle, number of completed packets} entry provided.
126 template <size_t kNumConnections>
SendNumberOfCompletedPackets(ProxyHost & proxy,FlatMap<uint16_t,uint16_t,kNumConnections> packets_per_connection)127 Status SendNumberOfCompletedPackets(
128 ProxyHost& proxy,
129 FlatMap<uint16_t, uint16_t, kNumConnections> packets_per_connection) {
130 std::array<
131 uint8_t,
132 emboss::NumberOfCompletedPacketsEvent::MinSizeInBytes() +
133 kNumConnections *
134 emboss::NumberOfCompletedPacketsEventData::IntrinsicSizeInBytes()>
135 hci_arr;
136 hci_arr.fill(0);
137 H4PacketWithHci nocp_event{emboss::H4PacketType::EVENT, hci_arr};
138 PW_TRY_ASSIGN(auto view,
139 MakeEmbossWriter<emboss::NumberOfCompletedPacketsEventWriter>(
140 nocp_event.GetHciSpan()));
141 view.header().event_code_enum().Write(
142 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
143 view.num_handles().Write(kNumConnections);
144
145 size_t i = 0;
146 for (const auto& [handle, num_packets] : packets_per_connection) {
147 view.nocp_data()[i].connection_handle().Write(handle);
148 view.nocp_data()[i].num_completed_packets().Write(num_packets);
149 ++i;
150 }
151
152 proxy.HandleH4HciFromController(std::move(nocp_event));
153 return OkStatus();
154 }
155
156 // Send a Connection_Complete event to `proxy` indicating the provided
157 // `handle` has disconnected.
SendConnectionCompleteEvent(ProxyHost & proxy,uint16_t handle,emboss::StatusCode status)158 Status SendConnectionCompleteEvent(ProxyHost& proxy,
159 uint16_t handle,
160 emboss::StatusCode status) {
161 std::array<uint8_t, emboss::ConnectionCompleteEvent::IntrinsicSizeInBytes()>
162 hci_arr_dc{};
163 H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc};
164 PW_TRY_ASSIGN(auto view,
165 MakeEmbossWriter<emboss::ConnectionCompleteEventWriter>(
166 dc_event.GetHciSpan()));
167 view.header().event_code_enum().Write(emboss::EventCode::CONNECTION_COMPLETE);
168 view.status().Write(status);
169 view.connection_handle().Write(handle);
170 proxy.HandleH4HciFromController(std::move(dc_event));
171 return OkStatus();
172 }
173
174 // Send a LE_Connection_Complete event to `proxy` indicating the provided
175 // `handle` has disconnected.
SendLeConnectionCompleteEvent(ProxyHost & proxy,uint16_t handle,emboss::StatusCode status)176 Status SendLeConnectionCompleteEvent(ProxyHost& proxy,
177 uint16_t handle,
178 emboss::StatusCode status) {
179 std::array<uint8_t,
180 emboss::LEConnectionCompleteSubevent::IntrinsicSizeInBytes()>
181 hci_arr_dc{};
182 H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc};
183 PW_TRY_ASSIGN(auto view,
184 MakeEmbossWriter<emboss::LEConnectionCompleteSubeventWriter>(
185 dc_event.GetHciSpan()));
186 view.le_meta_event().header().event_code_enum().Write(
187 emboss::EventCode::LE_META_EVENT);
188 view.le_meta_event().subevent_code_enum().Write(
189 emboss::LeSubEventCode::CONNECTION_COMPLETE);
190 view.status().Write(status);
191 view.connection_handle().Write(handle);
192 proxy.HandleH4HciFromController(std::move(dc_event));
193 return OkStatus();
194 }
195
196 // Send a Disconnection_Complete event to `proxy` indicating the provided
197 // `handle` has disconnected.
SendDisconnectionCompleteEvent(ProxyHost & proxy,uint16_t handle,bool successful=true)198 Status SendDisconnectionCompleteEvent(ProxyHost& proxy,
199 uint16_t handle,
200 bool successful = true) {
201 std::array<uint8_t,
202 emboss::DisconnectionCompleteEvent::IntrinsicSizeInBytes()>
203 hci_arr_dc;
204 hci_arr_dc.fill(0);
205 H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc};
206 PW_TRY_ASSIGN(auto view,
207 MakeEmbossWriter<emboss::DisconnectionCompleteEventWriter>(
208 dc_event.GetHciSpan()));
209 view.header().event_code_enum().Write(
210 emboss::EventCode::DISCONNECTION_COMPLETE);
211 view.status().Write(successful ? emboss::StatusCode::SUCCESS
212 : emboss::StatusCode::HARDWARE_FAILURE);
213 view.connection_handle().Write(handle);
214 proxy.HandleH4HciFromController(std::move(dc_event));
215 return OkStatus();
216 }
217
218 // Return a populated H4 event buffer of a type that proxy host doesn't interact
219 // with.
CreateNonInteractingToHostBuffer(H4PacketWithHci & h4_packet)220 Status CreateNonInteractingToHostBuffer(H4PacketWithHci& h4_packet) {
221 return CreateAndPopulateToHostEventView<emboss::InquiryCompleteEventWriter>(
222 h4_packet, emboss::EventCode::INQUIRY_COMPLETE)
223 .status();
224 }
225
226 struct CocParameters {
227 uint16_t handle = 123;
228 uint16_t local_cid = 234;
229 uint16_t remote_cid = 456;
230 uint16_t rx_mtu = 100;
231 uint16_t rx_mps = 100;
232 uint16_t rx_credits = 1;
233 uint16_t tx_mtu = 100;
234 uint16_t tx_mps = 100;
235 uint16_t tx_credits = 1;
236 pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn = nullptr;
237 pw::Function<void(L2capCoc::Event event)>&& event_fn = nullptr;
238 uint16_t rx_additional_credits = 0;
239 };
240
241 // Open and return an L2CAP connection-oriented channel managed by `proxy`.
BuildCoc(ProxyHost & proxy,CocParameters params)242 L2capCoc BuildCoc(ProxyHost& proxy, CocParameters params) {
243 pw::Result<L2capCoc> channel =
244 proxy.AcquireL2capCoc(params.handle,
245 {.cid = params.local_cid,
246 .mtu = params.rx_mtu,
247 .mps = params.rx_mps,
248 .credits = params.rx_credits},
249 {.cid = params.remote_cid,
250 .mtu = params.tx_mtu,
251 .mps = params.tx_mps,
252 .credits = params.tx_credits},
253 std::move(params.receive_fn),
254 std::move(params.event_fn),
255 params.rx_additional_credits);
256 return std::move(channel.value());
257 }
258
259 struct RfcommParameters {
260 uint16_t handle = 123;
261 RfcommChannel::Config rx_config = {
262 .cid = 234, .max_information_length = 900, .credits = 10};
263 RfcommChannel::Config tx_config = {
264 .cid = 456, .max_information_length = 900, .credits = 10};
265 uint8_t rfcomm_channel = 3;
266 };
267
BuildRfcomm(ProxyHost & proxy,RfcommParameters params)268 RfcommChannel BuildRfcomm(ProxyHost& proxy, RfcommParameters params) {
269 pw::Result<RfcommChannel> channel =
270 proxy.AcquireRfcommChannel(params.handle,
271 params.rx_config,
272 params.tx_config,
273 params.rfcomm_channel,
274 nullptr);
275 PW_TEST_EXPECT_OK(channel);
276 return std::move((channel.value()));
277 }
278
279 // ########## Examples
280
281 // Example for docs.rst.
TEST(Example,ExampleUsage)282 TEST(Example, ExampleUsage) {
283 // Populate H4 buffer to send towards controller.
284 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1>
285 h4_array_from_host{};
286 H4PacketWithH4 h4_packet_from_host{emboss::H4PacketType::UNKNOWN,
287 h4_array_from_host};
288 PW_TEST_EXPECT_OK(
289 PopulateNoninteractingToControllerBuffer(h4_packet_from_host));
290
291 // Populate H4 buffer to send towards host.
292 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes() + 1>
293 hci_array_from_controller{};
294 H4PacketWithHci h4_packet_from_controller{emboss::H4PacketType::UNKNOWN,
295 hci_array_from_controller};
296
297 PW_TEST_EXPECT_OK(
298 CreateNonInteractingToHostBuffer(h4_packet_from_controller));
299
300 pw::Function<void(H4PacketWithHci && packet)> container_send_to_host_fn(
301 []([[maybe_unused]] H4PacketWithHci&& packet) {});
302
303 pw::Function<void(H4PacketWithH4 && packet)> container_send_to_controller_fn(
304 ([]([[maybe_unused]] H4PacketWithH4&& packet) {}));
305
306 // DOCSTAG: [pw_bluetooth_proxy-examples-basic]
307
308 #include "pw_bluetooth_proxy/proxy_host.h"
309
310 // Container creates ProxyHost .
311 ProxyHost proxy = ProxyHost(std::move(container_send_to_host_fn),
312 std::move(container_send_to_controller_fn),
313 2);
314
315 // Container passes H4 packets from host through proxy. Proxy will in turn
316 // call the container-provided `container_send_to_controller_fn` to pass them
317 // on to the controller. Some packets may be modified, added, or removed.
318 proxy.HandleH4HciFromHost(std::move(h4_packet_from_host));
319
320 // Container passes H4 packets from controller through proxy. Proxy will in
321 // turn call the container-provided `container_send_to_host_fn` to pass them
322 // on to the controller. Some packets may be modified, added, or removed.
323 proxy.HandleH4HciFromController(std::move(h4_packet_from_controller));
324
325 // DOCSTAG: [pw_bluetooth_proxy-examples-basic]
326 }
327
328 // ########## PassthroughTest
329
330 // Verify buffer is properly passed (contents unaltered and zero-copy).
TEST(PassthroughTest,ToControllerPassesEqualBuffer)331 TEST(PassthroughTest, ToControllerPassesEqualBuffer) {
332 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr{};
333 H4PacketWithH4 h4_packet{emboss::H4PacketType::UNKNOWN, h4_arr};
334 PW_TEST_EXPECT_OK(PopulateNoninteractingToControllerBuffer(h4_packet));
335
336 // Struct for capturing because `pw::Function` can't fit multiple captures.
337 struct {
338 // Use a copy for comparison to catch if proxy incorrectly changes the
339 // passed buffer.
340 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr;
341 H4PacketWithH4* h4_packet;
342 uint8_t sends_called;
343 } send_capture = {h4_arr, &h4_packet, 0};
344
345 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
346 [&send_capture](H4PacketWithH4&& packet) {
347 send_capture.sends_called++;
348 EXPECT_EQ(packet.GetH4Type(),
349 emboss::H4PacketType(send_capture.h4_arr[0]));
350 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
351 send_capture.h4_packet->GetHciSpan().end(),
352 send_capture.h4_arr.begin() + 1,
353 send_capture.h4_arr.end()));
354 // Verify no copy by verifying buffer is at the same memory location.
355 EXPECT_EQ(packet.GetHciSpan().data(),
356 send_capture.h4_packet->GetHciSpan().data());
357 });
358
359 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
360 []([[maybe_unused]] H4PacketWithHci&& packet) {});
361
362 ProxyHost proxy = ProxyHost(
363 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
364
365 proxy.HandleH4HciFromHost(std::move(h4_packet));
366
367 // Verify to controller callback was called.
368 EXPECT_EQ(send_capture.sends_called, 1);
369 }
370
371 // Verify buffer is properly passed (contents unaltered and zero-copy).
TEST(PassthroughTest,ToHostPassesEqualBuffer)372 TEST(PassthroughTest, ToHostPassesEqualBuffer) {
373 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
374 hci_arr{};
375 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
376 PW_TEST_EXPECT_OK(CreateNonInteractingToHostBuffer(h4_packet));
377
378 // Struct for capturing because `pw::Function` can't fit multiple captures.
379 struct {
380 // Use a copy for comparison to catch if proxy incorrectly changes the
381 // passed buffer.
382 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
383 hci_arr;
384 H4PacketWithHci* h4_packet;
385 uint8_t sends_called;
386 } send_capture = {hci_arr, &h4_packet, 0};
387
388 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
389 [&send_capture](H4PacketWithHci&& packet) {
390 send_capture.sends_called++;
391 EXPECT_EQ(packet.GetH4Type(), send_capture.h4_packet->GetH4Type());
392 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
393 send_capture.h4_packet->GetHciSpan().end(),
394 send_capture.h4_packet->GetHciSpan().begin(),
395 send_capture.h4_packet->GetHciSpan().end()));
396 // Verify no copy by verifying buffer is at the same memory location.
397 EXPECT_EQ(packet.GetHciSpan().data(),
398 send_capture.h4_packet->GetHciSpan().data());
399 });
400
401 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
402 []([[maybe_unused]] H4PacketWithH4&& packet) {});
403
404 ProxyHost proxy = ProxyHost(
405 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
406
407 proxy.HandleH4HciFromController(std::move(h4_packet));
408
409 // Verify to controller callback was called.
410 EXPECT_EQ(send_capture.sends_called, 1);
411 }
412
413 // Verify a command complete event (of a type that proxy doesn't act on) is
414 // properly passed (contents unaltered and zero-copy).
TEST(PassthroughTest,ToHostPassesEqualCommandComplete)415 TEST(PassthroughTest, ToHostPassesEqualCommandComplete) {
416 std::array<
417 uint8_t,
418 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
419 hci_arr{};
420 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
421 PW_TEST_ASSERT_OK_AND_ASSIGN(
422 auto view,
423 CreateAndPopulateToHostEventView<
424 emboss::ReadLocalVersionInfoCommandCompleteEventWriter>(
425 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
426 view.command_complete().command_opcode_enum().Write(
427 emboss::OpCode::READ_LOCAL_VERSION_INFO);
428
429 // Struct for capturing because `pw::Function` can't fit multiple captures.
430 struct {
431 std::array<
432 uint8_t,
433 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
434 hci_arr;
435 H4PacketWithHci* h4_packet;
436 uint8_t sends_called;
437 } send_capture = {hci_arr, &h4_packet, 0};
438
439 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
440 [&send_capture](H4PacketWithHci&& packet) {
441 send_capture.sends_called++;
442 EXPECT_EQ(packet.GetH4Type(), send_capture.h4_packet->GetH4Type());
443 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
444 send_capture.h4_packet->GetHciSpan().end(),
445 send_capture.h4_packet->GetHciSpan().begin(),
446 send_capture.h4_packet->GetHciSpan().end()));
447 // Verify no copy by verifying buffer is at the same memory location.
448 EXPECT_EQ(packet.GetHciSpan().data(),
449 send_capture.h4_packet->GetHciSpan().data());
450 });
451
452 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
453 []([[maybe_unused]] H4PacketWithH4&& packet) {});
454
455 ProxyHost proxy = ProxyHost(
456 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
457
458 proxy.HandleH4HciFromController(std::move(h4_packet));
459
460 // Verify to controller callback was called.
461 EXPECT_EQ(send_capture.sends_called, 1);
462 }
463
464 // ########## BadPacketTest
465 // The proxy should not affect buffers it can't process (it should just pass
466 // them on).
467
TEST(BadPacketTest,BadH4TypeToControllerIsPassedOn)468 TEST(BadPacketTest, BadH4TypeToControllerIsPassedOn) {
469 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr{};
470 H4PacketWithH4 h4_packet{emboss::H4PacketType::UNKNOWN, h4_arr};
471 PW_TEST_EXPECT_OK(PopulateNoninteractingToControllerBuffer(h4_packet));
472 // Set back to an invalid type (after
473 // PopulateNoninteractingToControllerBuffer).
474 h4_packet.SetH4Type(emboss::H4PacketType::UNKNOWN);
475
476 // Struct for capturing because `pw::Function` can't fit multiple captures.
477 struct {
478 // Use a copy for comparison to catch if proxy incorrectly changes the
479 // passed buffer.
480 std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes() + 1> h4_arr;
481 H4PacketWithH4* h4_packet;
482 uint8_t sends_called;
483 } send_capture = {h4_arr, &h4_packet, 0};
484
485 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
486 [&send_capture](H4PacketWithH4&& packet) {
487 send_capture.sends_called++;
488 EXPECT_EQ(packet.GetH4Type(),
489 emboss::H4PacketType(send_capture.h4_arr[0]));
490 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
491 send_capture.h4_packet->GetHciSpan().end(),
492 send_capture.h4_arr.begin() + 1,
493 send_capture.h4_arr.end()));
494 // Verify no copy by verifying buffer is at the same memory location.
495 EXPECT_EQ(packet.GetHciSpan().data(),
496 send_capture.h4_packet->GetHciSpan().data());
497 });
498
499 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
500 []([[maybe_unused]] H4PacketWithHci&& packet) {});
501
502 ProxyHost proxy = ProxyHost(
503 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
504
505 proxy.HandleH4HciFromHost(std::move(h4_packet));
506
507 // Verify to controller callback was called.
508 EXPECT_EQ(send_capture.sends_called, 1);
509 }
510
TEST(BadPacketTest,BadH4TypeToHostIsPassedOn)511 TEST(BadPacketTest, BadH4TypeToHostIsPassedOn) {
512 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
513 hci_arr{};
514 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
515 PW_TEST_EXPECT_OK(CreateNonInteractingToHostBuffer(h4_packet));
516
517 // Set back to an invalid type.
518 h4_packet.SetH4Type(emboss::H4PacketType::UNKNOWN);
519
520 // Struct for capturing because `pw::Function` can't fit multiple captures.
521 struct {
522 // Use a copy for comparison to catch if proxy incorrectly changes the
523 // passed buffer.
524 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
525 hci_arr;
526 H4PacketWithHci* h4_packet;
527 uint8_t sends_called = 0;
528 } send_capture = {hci_arr, &h4_packet, 0};
529
530 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
531 [&send_capture](H4PacketWithHci&& packet) {
532 send_capture.sends_called++;
533 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::UNKNOWN);
534 EXPECT_TRUE(std::equal(send_capture.h4_packet->GetHciSpan().begin(),
535 send_capture.h4_packet->GetHciSpan().end(),
536 send_capture.h4_packet->GetHciSpan().begin(),
537 send_capture.h4_packet->GetHciSpan().end()));
538 // Verify no copy by verifying buffer is at the same memory location.
539 EXPECT_EQ(packet.GetHciSpan().data(),
540 send_capture.h4_packet->GetHciSpan().data());
541 });
542
543 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
544 []([[maybe_unused]] H4PacketWithH4&& packet) {});
545
546 ProxyHost proxy = ProxyHost(
547 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
548
549 proxy.HandleH4HciFromController(std::move(h4_packet));
550
551 // Verify to controller callback was called.
552 EXPECT_EQ(send_capture.sends_called, 1);
553 }
554
TEST(BadPacketTest,EmptyBufferToControllerIsPassedOn)555 TEST(BadPacketTest, EmptyBufferToControllerIsPassedOn) {
556 std::array<uint8_t, 0> h4_arr;
557 H4PacketWithH4 h4_packet{emboss::H4PacketType::COMMAND, h4_arr};
558 // H4PacketWithH4 use the underlying h4 buffer to store type. Since its length
559 // is zero, it can't store it and will always return UNKNOWN.
560 EXPECT_EQ(h4_packet.GetH4Type(), emboss::H4PacketType::UNKNOWN);
561
562 uint8_t sends_called = 0;
563 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
564 [&sends_called](H4PacketWithH4&& packet) {
565 sends_called++;
566 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::UNKNOWN);
567 EXPECT_TRUE(packet.GetHciSpan().empty());
568 });
569
570 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
571 []([[maybe_unused]] H4PacketWithHci&& packet) {});
572
573 ProxyHost proxy = ProxyHost(
574 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
575
576 proxy.HandleH4HciFromHost(std::move(h4_packet));
577
578 // Verify callback was called.
579 EXPECT_EQ(sends_called, 1);
580 }
581
TEST(BadPacketTest,EmptyBufferToHostIsPassedOn)582 TEST(BadPacketTest, EmptyBufferToHostIsPassedOn) {
583 std::array<uint8_t, 0> hci_arr;
584 H4PacketWithHci h4_packet{emboss::H4PacketType::EVENT, hci_arr};
585
586 uint8_t sends_called = 0;
587 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
588 [&sends_called](H4PacketWithHci&& packet) {
589 sends_called++;
590 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::EVENT);
591 EXPECT_TRUE(packet.GetHciSpan().empty());
592 });
593
594 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
595 []([[maybe_unused]] H4PacketWithH4&& packet) {});
596
597 ProxyHost proxy = ProxyHost(
598 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
599
600 proxy.HandleH4HciFromController(std::move(h4_packet));
601
602 // Verify callback was called.
603 EXPECT_EQ(sends_called, 1);
604 }
605
TEST(BadPacketTest,TooShortEventToHostIsPassOn)606 TEST(BadPacketTest, TooShortEventToHostIsPassOn) {
607 std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
608 valid_hci_arr{};
609 H4PacketWithHci valid_packet{emboss::H4PacketType::UNKNOWN, valid_hci_arr};
610 PW_TEST_EXPECT_OK(CreateNonInteractingToHostBuffer(valid_packet));
611
612 // Create packet for sending whose span size is one less than a valid command
613 // complete event.
614 H4PacketWithHci h4_packet{valid_packet.GetH4Type(),
615 valid_packet.GetHciSpan().subspan(
616 0, emboss::EventHeaderView::SizeInBytes() - 1)};
617
618 // Struct for capturing because `pw::Function` can't fit multiple captures.
619 struct {
620 std::array<uint8_t, emboss::EventHeaderView::SizeInBytes() - 1> hci_arr;
621 uint8_t sends_called = 0;
622 } send_capture;
623 // Copy valid event into a short_array whose size is one less than a valid
624 // EventHeader.
625 std::copy(h4_packet.GetHciSpan().begin(),
626 h4_packet.GetHciSpan().end(),
627 send_capture.hci_arr.begin());
628 send_capture.sends_called = 0;
629
630 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
631 [&send_capture](H4PacketWithHci&& packet) {
632 send_capture.sends_called++;
633 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
634 packet.GetHciSpan().end(),
635 send_capture.hci_arr.begin(),
636 send_capture.hci_arr.end()));
637 });
638
639 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
640 []([[maybe_unused]] H4PacketWithH4&& packet) {});
641
642 ProxyHost proxy = ProxyHost(
643 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
644
645 proxy.HandleH4HciFromController(std::move(h4_packet));
646
647 // Verify callback was called.
648 EXPECT_EQ(send_capture.sends_called, 1);
649 }
650
TEST(BadPacketTest,TooShortCommandCompleteEventToHost)651 TEST(BadPacketTest, TooShortCommandCompleteEventToHost) {
652 std::array<
653 uint8_t,
654 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
655 valid_hci_arr{};
656 H4PacketWithHci valid_packet{emboss::H4PacketType::UNKNOWN, valid_hci_arr};
657 PW_TEST_ASSERT_OK_AND_ASSIGN(
658 auto view,
659 CreateAndPopulateToHostEventView<
660 emboss::ReadLocalVersionInfoCommandCompleteEventWriter>(
661 valid_packet, emboss::EventCode::COMMAND_COMPLETE));
662 view.command_complete().command_opcode_enum().Write(
663 emboss::OpCode::READ_LOCAL_VERSION_INFO);
664
665 // Create packet for sending whose span size is one less than a valid command
666 // complete event.
667 H4PacketWithHci h4_packet{
668 valid_packet.GetH4Type(),
669 valid_packet.GetHciSpan().subspan(
670 0,
671 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::
672 SizeInBytes() -
673 1)};
674
675 // Struct for capturing because `pw::Function` capture can't fit multiple
676 // fields .
677 struct {
678 std::array<
679 uint8_t,
680 emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes() -
681 1>
682 hci_arr;
683 uint8_t sends_called = 0;
684 } send_capture;
685 std::copy(h4_packet.GetHciSpan().begin(),
686 h4_packet.GetHciSpan().end(),
687 send_capture.hci_arr.begin());
688 send_capture.sends_called = 0;
689
690 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
691 [&send_capture](H4PacketWithHci&& packet) {
692 send_capture.sends_called++;
693 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
694 packet.GetHciSpan().end(),
695 send_capture.hci_arr.begin(),
696 send_capture.hci_arr.end()));
697 });
698
699 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
700 []([[maybe_unused]] H4PacketWithH4&& packet) {});
701
702 ProxyHost proxy = ProxyHost(
703 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
704
705 proxy.HandleH4HciFromController(std::move(h4_packet));
706
707 // Verify callback was called.
708 EXPECT_EQ(send_capture.sends_called, 1);
709 }
710
711 // ########## ReserveLeAclCredits Tests
712
713 // Proxy Host should reserve requested ACL credits from controller's ACL credits
714 // when using ReadBufferSize command.
TEST(ReserveBrEdrAclCredits,ProxyCreditsReserveCreditsWithReadBufferSize)715 TEST(ReserveBrEdrAclCredits, ProxyCreditsReserveCreditsWithReadBufferSize) {
716 std::array<uint8_t,
717 emboss::ReadBufferSizeCommandCompleteEventWriter::SizeInBytes()>
718 hci_arr;
719 hci_arr.fill(0);
720 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
721 PW_TEST_ASSERT_OK_AND_ASSIGN(
722 auto view,
723 CreateAndPopulateToHostEventView<
724 emboss::ReadBufferSizeCommandCompleteEventWriter>(
725 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
726 view.command_complete().command_opcode_enum().Write(
727 emboss::OpCode::READ_BUFFER_SIZE);
728 view.total_num_acl_data_packets().Write(10);
729
730 uint8_t sends_called = 0;
731 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
732 [&sends_called](H4PacketWithHci&& received_packet) {
733 sends_called++;
734 PW_TEST_ASSERT_OK_AND_ASSIGN(
735 auto event_view,
736 MakeEmbossWriter<emboss::ReadBufferSizeCommandCompleteEventWriter>(
737 received_packet.GetHciSpan()));
738 // Should reserve 2 credits from original total of 10 (so 8 left for
739 // host).
740 EXPECT_EQ(event_view.total_num_acl_data_packets().Read(), 8);
741 });
742
743 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
744 []([[maybe_unused]] H4PacketWithH4&& packet) {});
745
746 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
747 std::move(send_to_controller_fn),
748 /*le_acl_credits_to_reserve=*/0,
749 /*br_edr_acl_credits_to_reserve=*/2);
750
751 proxy.HandleH4HciFromController(std::move(h4_packet));
752
753 EXPECT_EQ(proxy.GetNumFreeBrEdrAclPackets(), 2);
754
755 EXPECT_TRUE(proxy.HasSendBrEdrAclCapability());
756
757 // Verify to controller callback was called.
758 EXPECT_EQ(sends_called, 1);
759 }
760
761 // Proxy Host should reserve requested ACL LE credits from controller's ACL LE
762 // credits when using LEReadBufferSizeV1 command.
TEST(ReserveLeAclCredits,ProxyCreditsReserveCreditsWithLEReadBufferSizeV1)763 TEST(ReserveLeAclCredits, ProxyCreditsReserveCreditsWithLEReadBufferSizeV1) {
764 std::array<
765 uint8_t,
766 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
767 hci_arr{};
768 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
769 PW_TEST_ASSERT_OK_AND_ASSIGN(
770 auto view,
771 CreateAndPopulateToHostEventView<
772 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
773 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
774 view.command_complete().command_opcode_enum().Write(
775 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
776 view.total_num_le_acl_data_packets().Write(10);
777
778 uint8_t sends_called = 0;
779 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
780 [&sends_called](H4PacketWithHci&& received_packet) {
781 sends_called++;
782 PW_TEST_ASSERT_OK_AND_ASSIGN(
783 auto event_view,
784 MakeEmbossView<
785 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
786 received_packet.GetHciSpan()));
787
788 // Should reserve 2 credits from original total of 10 (so 8 left for
789 // host).
790 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 8);
791 });
792
793 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
794 []([[maybe_unused]] H4PacketWithH4&& packet) {});
795
796 ProxyHost proxy = ProxyHost(
797 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
798
799 proxy.HandleH4HciFromController(std::move(h4_packet));
800
801 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
802
803 EXPECT_TRUE(proxy.HasSendLeAclCapability());
804
805 // Verify to controller callback was called.
806 EXPECT_EQ(sends_called, 1);
807 }
808
809 // Proxy Host should reserve requested ACL LE credits from controller's ACL LE
810 // credits when using LEReadBufferSizeV2 command.
TEST(ReserveLeAclCredits,ProxyCreditsReserveCreditsWithLEReadBufferSizeV2)811 TEST(ReserveLeAclCredits, ProxyCreditsReserveCreditsWithLEReadBufferSizeV2) {
812 std::array<
813 uint8_t,
814 emboss::LEReadBufferSizeV2CommandCompleteEventWriter::SizeInBytes()>
815 hci_arr{};
816 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
817 PW_TEST_ASSERT_OK_AND_ASSIGN(
818 auto view,
819 CreateAndPopulateToHostEventView<
820 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
821 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
822 view.command_complete().command_opcode_enum().Write(
823 emboss::OpCode::LE_READ_BUFFER_SIZE_V2);
824 view.total_num_le_acl_data_packets().Write(10);
825
826 uint8_t sends_called = 0;
827 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
828 [&sends_called](H4PacketWithHci&& received_packet) {
829 sends_called++;
830 PW_TEST_ASSERT_OK_AND_ASSIGN(
831 auto event_view,
832 MakeEmbossView<
833 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
834 received_packet.GetHciSpan()));
835 // Should reserve 2 credits from original total of 10 (so 8 left for
836 // host).
837 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 8);
838 });
839
840 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
841 []([[maybe_unused]] H4PacketWithH4&& packet) {});
842
843 ProxyHost proxy = ProxyHost(
844 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
845
846 proxy.HandleH4HciFromController(std::move(h4_packet));
847
848 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
849
850 EXPECT_TRUE(proxy.HasSendLeAclCapability());
851
852 // Verify to controller callback was called.
853 EXPECT_EQ(sends_called, 1);
854 }
855
856 // If controller provides less than wanted credits, we should reserve that
857 // smaller amount.
TEST(ReserveLeAclCredits,ProxyCreditsCappedByControllerCredits)858 TEST(ReserveLeAclCredits, ProxyCreditsCappedByControllerCredits) {
859 std::array<
860 uint8_t,
861 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
862 hci_arr{};
863 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
864 PW_TEST_ASSERT_OK_AND_ASSIGN(
865 auto view,
866 CreateAndPopulateToHostEventView<
867 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
868 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
869 view.command_complete().command_opcode_enum().Write(
870 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
871 view.total_num_le_acl_data_packets().Write(5);
872
873 uint8_t sends_called = 0;
874 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
875 [&sends_called](H4PacketWithHci&& received_packet) {
876 sends_called++;
877 // We want 7, but can reserve only 5 from original 5 (so 0 left for
878 // host).
879 PW_TEST_ASSERT_OK_AND_ASSIGN(
880 auto event_view,
881 MakeEmbossView<
882 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
883 received_packet.GetHciSpan()));
884 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 0);
885 });
886
887 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
888 []([[maybe_unused]] H4PacketWithH4&& packet) {});
889
890 ProxyHost proxy = ProxyHost(
891 std::move(send_to_host_fn), std::move(send_to_controller_fn), 7);
892
893 proxy.HandleH4HciFromController(std::move(h4_packet));
894
895 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 5);
896
897 // Verify to controller callback was called.
898 EXPECT_EQ(sends_called, 1);
899 }
900
901 // Proxy Host can reserve zero credits from controller's ACL LE credits.
TEST(ReserveLeAclCredits,ProxyCreditsReserveZeroCredits)902 TEST(ReserveLeAclCredits, ProxyCreditsReserveZeroCredits) {
903 std::array<
904 uint8_t,
905 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
906 hci_arr{};
907 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
908 PW_TEST_ASSERT_OK_AND_ASSIGN(
909 auto view,
910 CreateAndPopulateToHostEventView<
911 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
912 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
913 view.command_complete().command_opcode_enum().Write(
914 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
915 view.total_num_le_acl_data_packets().Write(10);
916
917 uint8_t sends_called = 0;
918 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
919 [&sends_called](H4PacketWithHci&& received_packet) {
920 sends_called++;
921 PW_TEST_ASSERT_OK_AND_ASSIGN(
922 auto event_view,
923 MakeEmbossView<
924 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
925 received_packet.GetHciSpan()));
926 // Should reserve 0 credits from original total of 10 (so 10 left for
927 // host).
928 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 10);
929 });
930
931 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
932 []([[maybe_unused]] H4PacketWithH4&& packet) {});
933
934 ProxyHost proxy = ProxyHost(
935 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
936
937 proxy.HandleH4HciFromController(std::move(h4_packet));
938
939 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
940
941 EXPECT_FALSE(proxy.HasSendLeAclCapability());
942
943 // Verify to controller callback was called.
944 EXPECT_EQ(sends_called, 1);
945 }
946
947 // If controller has no credits, proxy should reserve none.
TEST(ReserveLeAclPackets,ProxyCreditsZeroWhenHostCreditsZero)948 TEST(ReserveLeAclPackets, ProxyCreditsZeroWhenHostCreditsZero) {
949 std::array<
950 uint8_t,
951 emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
952 hci_arr;
953 hci_arr.fill(0);
954 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
955 PW_TEST_ASSERT_OK_AND_ASSIGN(
956 auto view,
957 CreateAndPopulateToHostEventView<
958 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
959 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
960 view.command_complete().command_opcode_enum().Write(
961 emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
962 view.total_num_le_acl_data_packets().Write(0);
963
964 uint8_t sends_called = 0;
965 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
966 [&sends_called](H4PacketWithHci&& received_packet) {
967 sends_called++;
968 PW_TEST_ASSERT_OK_AND_ASSIGN(
969 auto event_view,
970 MakeEmbossView<
971 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
972 received_packet.GetHciSpan()));
973 // Should reserve 0 credit from original total of 0 (so 0 left for
974 // host).
975 EXPECT_EQ(event_view.total_num_le_acl_data_packets().Read(), 0);
976 });
977
978 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
979 []([[maybe_unused]] H4PacketWithH4&& packet) {});
980
981 ProxyHost proxy = ProxyHost(
982 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
983
984 proxy.HandleH4HciFromController(std::move(h4_packet));
985
986 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
987
988 EXPECT_TRUE(proxy.HasSendLeAclCapability());
989
990 // Verify to controller callback was called.
991 EXPECT_EQ(sends_called, 1);
992 }
993
TEST(ReserveLeAclPackets,ProxyCreditsZeroWhenNotInitialized)994 TEST(ReserveLeAclPackets, ProxyCreditsZeroWhenNotInitialized) {
995 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
996 []([[maybe_unused]] H4PacketWithHci&& packet) {});
997
998 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
999 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1000
1001 ProxyHost proxy = ProxyHost(
1002 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
1003
1004 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1005
1006 EXPECT_TRUE(proxy.HasSendLeAclCapability());
1007 }
1008
1009 // ########## GattNotifyTest
1010
TEST(GattNotifyTest,Send1ByteAttribute)1011 TEST(GattNotifyTest, Send1ByteAttribute) {
1012 struct {
1013 int sends_called = 0;
1014 // First four bits 0x0 encode PB & BC flags
1015 uint16_t handle = 0x0ACB;
1016 // Length of L2CAP PDU
1017 uint16_t acl_data_total_length = 0x0008;
1018 // Length of ATT PDU
1019 uint16_t pdu_length = 0x0004;
1020 // Attribute protocol channel ID (0x0004)
1021 uint16_t channel_id = 0x0004;
1022 // ATT_HANDLE_VALUE_NTF opcode 0x1B
1023 uint8_t attribute_opcode = 0x1B;
1024 uint16_t attribute_handle = 0x4321;
1025 std::array<uint8_t, 1> attribute_value = {0xFA};
1026
1027 // Built from the preceding values in little endian order.
1028 std::array<uint8_t, 12> expected_gatt_notify_packet = {
1029 0xCB, 0x0A, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x1B, 0x21, 0x43, 0xFA};
1030 } capture;
1031
1032 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1033 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1034
1035 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1036 [&capture](H4PacketWithH4&& packet) {
1037 capture.sends_called++;
1038 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
1039 EXPECT_EQ(packet.GetHciSpan().size(),
1040 capture.expected_gatt_notify_packet.size());
1041 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
1042 packet.GetHciSpan().end(),
1043 capture.expected_gatt_notify_packet.begin(),
1044 capture.expected_gatt_notify_packet.end()));
1045 PW_TEST_ASSERT_OK_AND_ASSIGN(
1046 auto acl,
1047 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
1048 emboss::BFrameView l2cap =
1049 emboss::MakeBFrameView(acl.payload().BackingStorage().data(),
1050 acl.data_total_length().Read());
1051 emboss::AttHandleValueNtfView gatt_notify =
1052 emboss::MakeAttHandleValueNtfView(
1053 capture.attribute_value.size(),
1054 l2cap.payload().BackingStorage().data(),
1055 l2cap.pdu_length().Read());
1056 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
1057 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
1058 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
1059 EXPECT_EQ(acl.header().broadcast_flag().Read(),
1060 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
1061 EXPECT_EQ(acl.data_total_length().Read(),
1062 capture.acl_data_total_length);
1063 EXPECT_EQ(l2cap.pdu_length().Read(), capture.pdu_length);
1064 EXPECT_EQ(l2cap.channel_id().Read(), capture.channel_id);
1065 EXPECT_EQ(gatt_notify.attribute_opcode().Read(),
1066 static_cast<emboss::AttOpcode>(capture.attribute_opcode));
1067 EXPECT_EQ(gatt_notify.attribute_handle().Read(),
1068 capture.attribute_handle);
1069 EXPECT_EQ(gatt_notify.attribute_value()[0].Read(),
1070 capture.attribute_value[0]);
1071 });
1072
1073 ProxyHost proxy = ProxyHost(
1074 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
1075 // Allow proxy to reserve 1 credit.
1076 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1077
1078 PW_TEST_EXPECT_OK(proxy.SendGattNotify(capture.handle,
1079 capture.attribute_handle,
1080 pw::span(capture.attribute_value)));
1081 EXPECT_EQ(capture.sends_called, 1);
1082 }
1083
TEST(GattNotifyTest,Send2ByteAttribute)1084 TEST(GattNotifyTest, Send2ByteAttribute) {
1085 struct {
1086 int sends_called = 0;
1087 // Max connection_handle value; first four bits 0x0 encode PB & BC flags
1088 const uint16_t handle = 0x0EFF;
1089 // Length of L2CAP PDU
1090 const uint16_t acl_data_total_length = 0x0009;
1091 // Length of ATT PDU
1092 const uint16_t pdu_length = 0x0005;
1093 // Attribute protocol channel ID (0x0004)
1094 const uint16_t channel_id = 0x0004;
1095 // ATT_HANDLE_VALUE_NTF opcode 0x1B
1096 const uint8_t attribute_opcode = 0x1B;
1097 const uint16_t attribute_handle = 0x1234;
1098 const std::array<uint8_t, 2> attribute_value = {0xAB, 0xCD};
1099
1100 // Built from the preceding values in little endian order.
1101 const std::array<uint8_t, 13> expected_gatt_notify_packet = {0xFF,
1102 0x0E,
1103 0x09,
1104 0x00,
1105 0x05,
1106 0x00,
1107 0x04,
1108 0x00,
1109 0x1B,
1110 0x34,
1111 0x12,
1112 0xAB,
1113 0XCD};
1114 } capture;
1115
1116 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1117 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1118
1119 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
1120 [&capture](H4PacketWithH4&& packet) {
1121 ++capture.sends_called;
1122 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
1123 EXPECT_EQ(packet.GetHciSpan().size(),
1124 capture.expected_gatt_notify_packet.size());
1125 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
1126 packet.GetHciSpan().end(),
1127 capture.expected_gatt_notify_packet.begin(),
1128 capture.expected_gatt_notify_packet.end()));
1129 PW_TEST_ASSERT_OK_AND_ASSIGN(
1130 auto acl,
1131 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
1132 emboss::BFrameView l2cap = emboss::MakeBFrameView(
1133 acl.payload().BackingStorage().data(), acl.SizeInBytes());
1134 emboss::AttHandleValueNtfView gatt_notify =
1135 emboss::MakeAttHandleValueNtfView(
1136 capture.attribute_value.size(),
1137 l2cap.payload().BackingStorage().data(),
1138 l2cap.pdu_length().Read());
1139 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
1140 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
1141 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
1142 EXPECT_EQ(acl.header().broadcast_flag().Read(),
1143 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
1144 EXPECT_EQ(acl.data_total_length().Read(),
1145 capture.acl_data_total_length);
1146 EXPECT_EQ(l2cap.pdu_length().Read(), capture.pdu_length);
1147 EXPECT_EQ(l2cap.channel_id().Read(), capture.channel_id);
1148 EXPECT_EQ(gatt_notify.attribute_opcode().Read(),
1149 static_cast<emboss::AttOpcode>(capture.attribute_opcode));
1150 EXPECT_EQ(gatt_notify.attribute_handle().Read(),
1151 capture.attribute_handle);
1152 EXPECT_EQ(gatt_notify.attribute_value()[0].Read(),
1153 capture.attribute_value[0]);
1154 EXPECT_EQ(gatt_notify.attribute_value()[1].Read(),
1155 capture.attribute_value[1]);
1156 });
1157
1158 ProxyHost proxy = ProxyHost(
1159 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
1160 // Allow proxy to reserve 1 credit.
1161 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1162
1163 PW_TEST_EXPECT_OK(proxy.SendGattNotify(capture.handle,
1164 capture.attribute_handle,
1165 pw::span(capture.attribute_value)));
1166 EXPECT_EQ(capture.sends_called, 1);
1167 }
1168
TEST(GattNotifyTest,ReturnsErrorIfAttributeTooLarge)1169 TEST(GattNotifyTest, ReturnsErrorIfAttributeTooLarge) {
1170 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1171 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1172 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1173 []([[maybe_unused]] H4PacketWithH4 packet) { FAIL(); });
1174
1175 ProxyHost proxy = ProxyHost(
1176 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
1177
1178 // attribute_value 1 byte too large
1179 std::array<uint8_t,
1180 proxy.GetMaxAclSendSize() -
1181 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() -
1182 emboss::BasicL2capHeader::IntrinsicSizeInBytes() -
1183 emboss::AttHandleValueNtf::MinSizeInBytes() + 1>
1184 attribute_value_too_large;
1185 EXPECT_EQ(proxy.SendGattNotify(123, 456, attribute_value_too_large),
1186 PW_STATUS_INVALID_ARGUMENT);
1187 }
1188
TEST(GattNotifyTest,ChannelIsNotConstructedIfParametersInvalid)1189 TEST(GattNotifyTest, ChannelIsNotConstructedIfParametersInvalid) {
1190 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1191 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1192 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1193 []([[maybe_unused]] H4PacketWithH4 packet) { FAIL(); });
1194
1195 ProxyHost proxy = ProxyHost(
1196 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
1197
1198 EXPECT_EQ(proxy.SendGattNotify(123, 0, {}), PW_STATUS_INVALID_ARGUMENT);
1199 // connection_handle too large
1200 EXPECT_EQ(proxy.SendGattNotify(0x0FFF, 345, {}), PW_STATUS_INVALID_ARGUMENT);
1201 }
1202
1203 // ########## NumberOfCompletedPacketsTest
1204
TEST(NumberOfCompletedPacketsTest,TwoOfThreeSentPacketsComplete)1205 TEST(NumberOfCompletedPacketsTest, TwoOfThreeSentPacketsComplete) {
1206 constexpr size_t kNumConnections = 3;
1207 struct {
1208 int sends_called = 0;
1209 const std::array<uint16_t, kNumConnections> connection_handles = {
1210 0x123, 0x456, 0x789};
1211 } capture;
1212
1213 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1214 [&capture](H4PacketWithHci&& packet) {
1215 PW_TEST_ASSERT_OK_AND_ASSIGN(
1216 auto event_header,
1217 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1218 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1219 capture.sends_called++;
1220 if (event_header.event_code_enum().Read() !=
1221 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1222 return;
1223 }
1224
1225 PW_TEST_ASSERT_OK_AND_ASSIGN(
1226 auto view,
1227 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1228 packet.GetHciSpan()));
1229 EXPECT_EQ(packet.GetHciSpan().size(), 15ul);
1230 EXPECT_EQ(view.num_handles().Read(), capture.connection_handles.size());
1231 EXPECT_EQ(view.header().event_code_enum().Read(),
1232 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1233
1234 // Proxy should have reclaimed 1 credit from Connection 0 (leaving 0
1235 // credits in packet), no credits from Connection 1 (meaning 0 will be
1236 // unchanged), and 1 credit from Connection 2 (leaving 0).
1237 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1238 capture.connection_handles[0]);
1239 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 0);
1240
1241 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
1242 capture.connection_handles[1]);
1243 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 0);
1244
1245 EXPECT_EQ(view.nocp_data()[2].connection_handle().Read(),
1246 capture.connection_handles[2]);
1247 EXPECT_EQ(view.nocp_data()[2].num_completed_packets().Read(), 0);
1248 });
1249 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1250 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1251
1252 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1253 std::move(send_to_controller_fn),
1254 kNumConnections);
1255 PW_TEST_EXPECT_OK(
1256 SendLeReadBufferResponseFromController(proxy, kNumConnections));
1257 EXPECT_EQ(capture.sends_called, 1);
1258
1259 std::array<uint8_t, 1> attribute_value = {0};
1260
1261 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 3);
1262
1263 // Send packet; num free packets should decrement.
1264 EXPECT_TRUE(proxy
1265 .SendGattNotify(capture.connection_handles[0],
1266 1,
1267 pw::span(attribute_value))
1268 .ok());
1269 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1270 // Proxy host took all credits so will not pass NOCP on to host.
1271 EXPECT_EQ(capture.sends_called, 1);
1272
1273 // Send packet over Connection 1, which will not have a packet completed in
1274 // the Number_of_Completed_Packets event.
1275 EXPECT_TRUE(proxy
1276 .SendGattNotify(capture.connection_handles[1],
1277 1,
1278 pw::span(attribute_value))
1279 .ok());
1280 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1281
1282 // Send third packet; num free packets should decrement again.
1283 EXPECT_TRUE(proxy
1284 .SendGattNotify(capture.connection_handles[2],
1285 1,
1286 pw::span(attribute_value))
1287 .ok());
1288 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1289
1290 // Send Number_of_Completed_Packets event that reports 1 packet on Connection
1291 // 0, 0 packets on Connection 1, and 1 packet on Connection 2. Checks in
1292 // send_to_host_fn will ensure we have reclaimed 2 of 3 credits.
1293 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1294 proxy,
1295 FlatMap<uint16_t, uint16_t, 3>({{{capture.connection_handles[0], 1},
1296 {capture.connection_handles[1], 0},
1297 {capture.connection_handles[2], 1}}})));
1298 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1299 // Proxy host took all credits so will not pass NOCP event on to host.
1300 EXPECT_EQ(capture.sends_called, 1);
1301 }
1302
TEST(NumberOfCompletedPacketsTest,ManyMorePacketsCompletedThanPacketsPending)1303 TEST(NumberOfCompletedPacketsTest, ManyMorePacketsCompletedThanPacketsPending) {
1304 constexpr size_t kNumConnections = 2;
1305 struct {
1306 int sends_called = 0;
1307 const std::array<uint16_t, kNumConnections> connection_handles = {0x123,
1308 0x456};
1309 } capture;
1310
1311 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1312 [&capture](H4PacketWithHci&& packet) {
1313 PW_TEST_ASSERT_OK_AND_ASSIGN(
1314 auto event_header,
1315 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1316 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1317 capture.sends_called++;
1318 if (event_header.event_code_enum().Read() !=
1319 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1320 return;
1321 }
1322
1323 PW_TEST_ASSERT_OK_AND_ASSIGN(
1324 auto view,
1325 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1326 packet.GetHciSpan()));
1327 EXPECT_EQ(packet.GetHciSpan().size(), 11ul);
1328 EXPECT_EQ(view.num_handles().Read(), capture.connection_handles.size());
1329 EXPECT_EQ(view.header().event_code_enum().Read(),
1330 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1331
1332 // Proxy should have reclaimed 1 credit from Connection 0 (leaving
1333 // 9 credits in packet) and 1 credit from Connection 2 (leaving 14).
1334 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1335 capture.connection_handles[0]);
1336 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 9);
1337
1338 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
1339 capture.connection_handles[1]);
1340 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 14);
1341 });
1342 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1343 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1344
1345 ProxyHost proxy = ProxyHost(
1346 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
1347 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1348 EXPECT_EQ(capture.sends_called, 1);
1349
1350 std::array<uint8_t, 1> attribute_value = {0};
1351
1352 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1353
1354 // Send packet over Connection 0; num free packets should decrement.
1355 EXPECT_TRUE(proxy
1356 .SendGattNotify(capture.connection_handles[0],
1357 1,
1358 pw::span(attribute_value))
1359 .ok());
1360 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1361
1362 // Send packet over Connection 1; num free packets should decrement again.
1363 EXPECT_TRUE(proxy
1364 .SendGattNotify(capture.connection_handles[1],
1365 1,
1366 pw::span(attribute_value))
1367 .ok());
1368 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1369
1370 // Send Number_of_Completed_Packets event that reports 10 packets on
1371 // Connection 0 and 15 packets on Connection 1. Checks in send_to_host_fn
1372 // will ensure we have reclaimed exactly 2 credits, 1 from each Connection.
1373 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1374 proxy,
1375 FlatMap<uint16_t, uint16_t, 2>({{{capture.connection_handles[0], 10},
1376 {capture.connection_handles[1], 15}}})));
1377 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1378 EXPECT_EQ(capture.sends_called, 2);
1379 }
1380
TEST(NumberOfCompletedPacketsTest,ProxyReclaimsOnlyItsUsedCredits)1381 TEST(NumberOfCompletedPacketsTest, ProxyReclaimsOnlyItsUsedCredits) {
1382 constexpr size_t kNumConnections = 2;
1383 struct {
1384 int sends_called = 0;
1385 const std::array<uint16_t, kNumConnections> connection_handles = {0x123,
1386 0x456};
1387 } capture;
1388
1389 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1390 [&capture](H4PacketWithHci&& packet) {
1391 PW_TEST_ASSERT_OK_AND_ASSIGN(
1392 auto event_header,
1393 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1394 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1395 capture.sends_called++;
1396 if (event_header.event_code_enum().Read() !=
1397 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1398 return;
1399 }
1400
1401 PW_TEST_ASSERT_OK_AND_ASSIGN(
1402 auto view,
1403 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1404 packet.GetHciSpan()));
1405 EXPECT_EQ(packet.GetHciSpan().size(), 11ul);
1406 EXPECT_EQ(view.num_handles().Read(), 2);
1407 EXPECT_EQ(view.header().event_code_enum().Read(),
1408 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1409
1410 // Proxy has 4 credits it wants to reclaim, but it should have only
1411 // reclaimed the 2 credits it used on Connection 0.
1412 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1413 capture.connection_handles[0]);
1414 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 8);
1415 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
1416 capture.connection_handles[1]);
1417 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 15);
1418 });
1419 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1420 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1421
1422 ProxyHost proxy = ProxyHost(
1423 std::move(send_to_host_fn), std::move(send_to_controller_fn), 4);
1424 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 4));
1425 EXPECT_EQ(capture.sends_called, 1);
1426
1427 std::array<uint8_t, 1> attribute_value = {0};
1428
1429 // Use 2 credits on Connection 0 and 2 credits on random connections that will
1430 // not be included in the NOCP event.
1431 EXPECT_TRUE(proxy
1432 .SendGattNotify(capture.connection_handles[0],
1433 1,
1434 pw::span(attribute_value))
1435 .ok());
1436 EXPECT_TRUE(proxy
1437 .SendGattNotify(capture.connection_handles[0],
1438 1,
1439 pw::span(attribute_value))
1440 .ok());
1441 EXPECT_TRUE(proxy.SendGattNotify(0xABC, 1, pw::span(attribute_value)).ok());
1442 EXPECT_TRUE(proxy.SendGattNotify(0xBCD, 1, pw::span(attribute_value)).ok());
1443 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1444
1445 // Send Number_of_Completed_Packets event that reports 10 packets on
1446 // Connection 0 and 15 packets on Connection 1. Checks in send_to_host_fn
1447 // will ensure we have reclaimed only 2 credits.
1448 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1449 proxy,
1450 FlatMap<uint16_t, uint16_t, 2>({{{capture.connection_handles[0], 10},
1451 {capture.connection_handles[1], 15}}})));
1452 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
1453 // NOCP has credits remaining so will be passed on to host.
1454 EXPECT_EQ(capture.sends_called, 2);
1455 }
1456
TEST(NumberOfCompletedPacketsTest,EventUnmodifiedIfNoCreditsInUse)1457 TEST(NumberOfCompletedPacketsTest, EventUnmodifiedIfNoCreditsInUse) {
1458 constexpr size_t kNumConnections = 2;
1459 struct {
1460 int sends_called = 0;
1461 const std::array<uint16_t, kNumConnections> connection_handles = {0x123,
1462 0x456};
1463 } capture;
1464
1465 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1466 [&capture](H4PacketWithHci&& packet) {
1467 PW_TEST_ASSERT_OK_AND_ASSIGN(
1468 auto event_header,
1469 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1470 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1471 capture.sends_called++;
1472 if (event_header.event_code_enum().Read() !=
1473 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1474 return;
1475 }
1476
1477 PW_TEST_ASSERT_OK_AND_ASSIGN(
1478 auto view,
1479 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1480 packet.GetHciSpan()));
1481 EXPECT_EQ(packet.GetHciSpan().size(), 11ul);
1482 EXPECT_EQ(view.num_handles().Read(), 2);
1483 EXPECT_EQ(view.header().event_code_enum().Read(),
1484 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1485
1486 // Event should be unmodified.
1487 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1488 capture.connection_handles[0]);
1489 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 10);
1490 EXPECT_EQ(view.nocp_data()[1].connection_handle().Read(),
1491 capture.connection_handles[1]);
1492 EXPECT_EQ(view.nocp_data()[1].num_completed_packets().Read(), 15);
1493 });
1494 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1495 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1496
1497 ProxyHost proxy = ProxyHost(
1498 std::move(send_to_host_fn), std::move(send_to_controller_fn), 10);
1499 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 10));
1500 EXPECT_EQ(capture.sends_called, 1);
1501
1502 // Send Number_of_Completed_Packets event that reports 10 packets on
1503 // Connection 0 and 15 packets on Connection 1. Checks in send_to_host_fn
1504 // will ensure we have not modified the NOCP event.
1505 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1506 proxy,
1507 FlatMap<uint16_t, uint16_t, 2>({{{capture.connection_handles[0], 10},
1508 {capture.connection_handles[1], 15}}})));
1509 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 10);
1510 // NOCP has credits remaining so will be passed on to host.
1511 EXPECT_EQ(capture.sends_called, 2);
1512 }
1513
TEST(NumberOfCompletedPacketsTest,HandlesUnusualEvents)1514 TEST(NumberOfCompletedPacketsTest, HandlesUnusualEvents) {
1515 constexpr size_t kNumConnections = 5;
1516 struct {
1517 int sends_called = 0;
1518 const std::array<uint16_t, kNumConnections> connection_handles = {
1519 0x123, 0x234, 0x345, 0x456, 0x567};
1520 } capture;
1521
1522 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1523 [&capture](H4PacketWithHci&& packet) {
1524 PW_TEST_ASSERT_OK_AND_ASSIGN(
1525 auto event_header,
1526 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1527 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1528 capture.sends_called++;
1529 if (event_header.event_code_enum().Read() !=
1530 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1531 return;
1532 }
1533
1534 PW_TEST_ASSERT_OK_AND_ASSIGN(
1535 auto view,
1536 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1537 packet.GetHciSpan()));
1538 if (view.num_handles().Read() == 0) {
1539 return;
1540 }
1541
1542 EXPECT_EQ(packet.GetHciSpan().size(), 23ul);
1543 EXPECT_EQ(view.num_handles().Read(), 5);
1544 EXPECT_EQ(view.header().event_code_enum().Read(),
1545 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1546
1547 // Event should be unmodified.
1548 for (int i = 0; i < 5; ++i) {
1549 EXPECT_EQ(view.nocp_data()[i].connection_handle().Read(),
1550 capture.connection_handles[i]);
1551 EXPECT_EQ(view.nocp_data()[i].num_completed_packets().Read(), 0);
1552 }
1553 });
1554 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1555 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1556
1557 ProxyHost proxy = ProxyHost(
1558 std::move(send_to_host_fn), std::move(send_to_controller_fn), 10);
1559 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 10));
1560 EXPECT_EQ(capture.sends_called, 1);
1561
1562 // Send Number_of_Completed_Packets event with no entries.
1563 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1564 proxy, FlatMap<uint16_t, uint16_t, 0>({{}})));
1565 // NOCP has no entries, so will not be passed on to host.
1566 EXPECT_EQ(capture.sends_called, 1);
1567
1568 // Send Number_of_Completed_Packets event that reports 0 packets for various
1569 // connections.
1570 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1571 proxy,
1572 FlatMap<uint16_t, uint16_t, 5>({{{capture.connection_handles[0], 0},
1573 {capture.connection_handles[1], 0},
1574 {capture.connection_handles[2], 0},
1575 {capture.connection_handles[3], 0},
1576 {capture.connection_handles[4], 0}}})));
1577 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 10);
1578 // Proxy host will not pass on a NOCP with no credits.
1579 EXPECT_EQ(capture.sends_called, 1);
1580 }
1581
TEST(NumberOfCompletedPacketsTest,MultipleChannelsDifferentTransports)1582 TEST(NumberOfCompletedPacketsTest, MultipleChannelsDifferentTransports) {
1583 static constexpr size_t kPayloadSize = 3;
1584 struct {
1585 int sends_called = 0;
1586 std::array<uint8_t, kPayloadSize> payload = {
1587 0xAB,
1588 0xCD,
1589 0xEF,
1590 };
1591 } capture;
1592
1593 pw::Function<void(H4PacketWithHci&&)>&& send_to_host_fn(
1594 [](H4PacketWithHci&&) {});
1595 pw::Function<void(H4PacketWithH4&&)>&& send_to_controller_fn(
1596 [&capture](H4PacketWithH4&&) { ++capture.sends_called; });
1597
1598 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1599 std::move(send_to_controller_fn),
1600 /*le_acl_credits_to_reserve=*/1,
1601 /*br_edr_acl_credits_to_reserve=*/1);
1602 // Allow proxy to reserve BR/EDR 1 credit.
1603 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
1604 // Allow proxy to reserve LE 1 credit.
1605 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1606
1607 // Test that sending on one type of transport doesn't get blocked if the other
1608 // type of transport is out of credits.
1609
1610 L2capCoc le_channel =
1611 BuildCoc(proxy, CocParameters{.handle = 0x123, .tx_credits = 2});
1612 EXPECT_EQ(le_channel.Write(capture.payload), PW_STATUS_OK);
1613 EXPECT_EQ(capture.sends_called, 1);
1614
1615 RfcommChannel bredr_channel =
1616 BuildRfcomm(proxy, RfcommParameters{.handle = 0x456});
1617 EXPECT_EQ(bredr_channel.Write(capture.payload), PW_STATUS_OK);
1618 // Send should succeed even though no LE credits available
1619 EXPECT_EQ(capture.sends_called, 2);
1620
1621 // Queue an LE write
1622 EXPECT_EQ(le_channel.Write(capture.payload), PW_STATUS_OK);
1623 EXPECT_EQ(capture.sends_called, 2);
1624
1625 // Complete previous LE write
1626 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1627 proxy, FlatMap<uint16_t, uint16_t, 1>({{{0x123, 1}}})));
1628 EXPECT_EQ(capture.sends_called, 3);
1629
1630 // Complete BR/EDR write
1631 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1632 proxy, FlatMap<uint16_t, uint16_t, 1>({{{0x456, 1}}})));
1633
1634 // Write again
1635 EXPECT_EQ(bredr_channel.Write(capture.payload), PW_STATUS_OK);
1636 EXPECT_EQ(capture.sends_called, 4);
1637 }
1638
1639 // ########## DisconnectionCompleteTest
1640
TEST(DisconnectionCompleteTest,DisconnectionReclaimsCredits)1641 TEST(DisconnectionCompleteTest, DisconnectionReclaimsCredits) {
1642 struct {
1643 int sends_called = 0;
1644 uint16_t connection_handle = 0x123;
1645 } capture;
1646
1647 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1648 [&capture](H4PacketWithHci&& packet) {
1649 PW_TEST_ASSERT_OK_AND_ASSIGN(
1650 auto event_header,
1651 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1652 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1653 capture.sends_called++;
1654 if (event_header.event_code_enum().Read() !=
1655 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1656 return;
1657 }
1658
1659 PW_TEST_ASSERT_OK_AND_ASSIGN(
1660 auto view,
1661 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1662 packet.GetHciSpan()));
1663 EXPECT_EQ(packet.GetHciSpan().size(), 7ul);
1664 EXPECT_EQ(view.num_handles().Read(), 1);
1665 EXPECT_EQ(view.header().event_code_enum().Read(),
1666 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1667
1668 // Event should be unmodified.
1669 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1670 capture.connection_handle);
1671 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 10);
1672 });
1673 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1674 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1675
1676 ProxyHost proxy = ProxyHost(
1677 std::move(send_to_host_fn), std::move(send_to_controller_fn), 10);
1678 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 10));
1679 EXPECT_EQ(capture.sends_called, 1);
1680
1681 std::array<uint8_t, 1> attribute_value = {0};
1682
1683 // Use up 3 of the 10 credits on the Connection that will be disconnected.
1684 for (int i = 0; i < 3; ++i) {
1685 EXPECT_TRUE(proxy
1686 .SendGattNotify(
1687 capture.connection_handle, 1, pw::span(attribute_value))
1688 .ok());
1689 }
1690 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 7);
1691 // Use up 2 credits on a random Connection.
1692 for (int i = 0; i < 2; ++i) {
1693 EXPECT_TRUE(proxy.SendGattNotify(0x456, 1, pw::span(attribute_value)).ok());
1694 }
1695 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 5);
1696
1697 // Send Disconnection_Complete event, which should reclaim 3 credits.
1698 PW_TEST_EXPECT_OK(
1699 SendDisconnectionCompleteEvent(proxy, capture.connection_handle));
1700 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 8);
1701
1702 // Use 1 credit and reclaim it on a bunch of random channels. Then send
1703 // disconnect and ensure it was cleaned up in connections list. The send will
1704 // fail if disconnect doesn't cleanup properly.
1705 //
1706 // We already have an active connection at this point in the test, so loop
1707 // over the remaining slots + 1 which would otherwise fail if cleanup wasn't
1708 // working right.
1709 for (uint16_t i = 0; i < kMaxProxyActiveConnections; ++i) {
1710 uint16_t handle = 0x234 + i;
1711 EXPECT_TRUE(
1712 proxy.SendGattNotify(handle, 1, pw::span(attribute_value)).ok());
1713 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1714 proxy, FlatMap<uint16_t, uint16_t, 1>({{{handle, 1}}})));
1715 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 8);
1716 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, handle));
1717 }
1718
1719 // Send Number_of_Completed_Packets event that reports 10 packets, none of
1720 // which should be reclaimed because this Connection has disconnected. Checks
1721 // in send_to_host_fn will ensure we have not modified the NOCP event.
1722 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1723 proxy,
1724 FlatMap<uint16_t, uint16_t, 1>({{{capture.connection_handle, 10}}})));
1725 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 8);
1726 // NOCP has credits remaining so will be passed on to host.
1727 EXPECT_EQ(capture.sends_called, 13);
1728 }
1729
TEST(DisconnectionCompleteTest,FailedDisconnectionHasNoEffect)1730 TEST(DisconnectionCompleteTest, FailedDisconnectionHasNoEffect) {
1731 uint16_t connection_handle = 0x123;
1732
1733 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1734 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1735 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1736 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1737
1738 ProxyHost proxy = ProxyHost(
1739 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
1740 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1741
1742 std::array<uint8_t, 1> attribute_value = {0};
1743
1744 // Use sole credit.
1745 EXPECT_TRUE(
1746 proxy.SendGattNotify(connection_handle, 1, pw::span(attribute_value))
1747 .ok());
1748 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1749
1750 // Send failed Disconnection_Complete event, should not reclaim credit.
1751 PW_TEST_EXPECT_OK(
1752 SendDisconnectionCompleteEvent(proxy,
1753 connection_handle, /*successful=*/
1754 false));
1755 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1756 }
1757
TEST(DisconnectionCompleteTest,DisconnectionOfUnusedConnectionHasNoEffect)1758 TEST(DisconnectionCompleteTest, DisconnectionOfUnusedConnectionHasNoEffect) {
1759 uint16_t connection_handle = 0x123;
1760
1761 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1762 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1763 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1764 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1765
1766 ProxyHost proxy = ProxyHost(
1767 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
1768 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1769
1770 std::array<uint8_t, 1> attribute_value = {0};
1771
1772 // Use sole credit.
1773 EXPECT_TRUE(
1774 proxy.SendGattNotify(connection_handle, 1, pw::span(attribute_value))
1775 .ok());
1776 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1777
1778 // Send Disconnection_Complete event to random Connection, should have no
1779 // effect.
1780 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, 0x456));
1781 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1782 }
1783
TEST(DisconnectionCompleteTest,CanReuseConnectionHandleAfterDisconnection)1784 TEST(DisconnectionCompleteTest, CanReuseConnectionHandleAfterDisconnection) {
1785 struct {
1786 int sends_called = 0;
1787 uint16_t connection_handle = 0x123;
1788 } capture;
1789
1790 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1791 [&capture](H4PacketWithHci&& packet) {
1792 PW_TEST_ASSERT_OK_AND_ASSIGN(
1793 auto event_header,
1794 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1795 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1796 capture.sends_called++;
1797 if (event_header.event_code_enum().Read() !=
1798 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1799 return;
1800 }
1801
1802 PW_TEST_ASSERT_OK_AND_ASSIGN(
1803 auto view,
1804 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1805 packet.GetHciSpan()));
1806 EXPECT_EQ(packet.GetHciSpan().size(), 7ul);
1807 EXPECT_EQ(view.num_handles().Read(), 1);
1808 EXPECT_EQ(view.header().event_code_enum().Read(),
1809 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1810
1811 // Should have reclaimed the 1 packet.
1812 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1813 capture.connection_handle);
1814 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 0);
1815 });
1816 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1817 []([[maybe_unused]] H4PacketWithH4&& packet) {});
1818
1819 ProxyHost proxy = ProxyHost(
1820 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
1821 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1822 EXPECT_EQ(capture.sends_called, 1);
1823
1824 std::array<uint8_t, 1> attribute_value = {0};
1825
1826 // Establish connection over `connection_handle`.
1827 EXPECT_TRUE(proxy
1828 .SendGattNotify(
1829 capture.connection_handle, 1, pw::span(attribute_value))
1830 .ok());
1831 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1832
1833 // Disconnect `connection_handle`.
1834 PW_TEST_EXPECT_OK(
1835 SendDisconnectionCompleteEvent(proxy, capture.connection_handle));
1836 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1837 EXPECT_EQ(capture.sends_called, 2);
1838
1839 // Re-establish connection over `connection_handle`.
1840 EXPECT_TRUE(proxy
1841 .SendGattNotify(
1842 capture.connection_handle, 1, pw::span(attribute_value))
1843 .ok());
1844 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1845
1846 // Send Number_of_Completed_Packets event that reports 1 packet. Checks in
1847 // send_to_host_fn will ensure packet has been reclaimed.
1848 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1849 proxy,
1850 FlatMap<uint16_t, uint16_t, 1>({{{capture.connection_handle, 1}}})));
1851 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1852 // Since proxy reclaimed the one credit, it does not pass event on to host.
1853 EXPECT_EQ(capture.sends_called, 2);
1854 }
1855
1856 // ########## ResetTest
1857
TEST(ResetTest,ResetClearsActiveConnections)1858 TEST(ResetTest, ResetClearsActiveConnections) {
1859 struct {
1860 int sends_called = 0;
1861 const uint16_t connection_handle = 0x123;
1862 } host_capture;
1863 struct {
1864 int sends_called = 0;
1865 const uint16_t connection_handle = 0x123;
1866 } controller_capture;
1867
1868 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1869 [&host_capture](H4PacketWithHci&& packet) {
1870 PW_TEST_ASSERT_OK_AND_ASSIGN(
1871 auto event_header,
1872 MakeEmbossView<emboss::EventHeaderView>(packet.GetHciSpan().subspan(
1873 0, emboss::EventHeader::IntrinsicSizeInBytes())));
1874 host_capture.sends_called++;
1875 if (event_header.event_code_enum().Read() !=
1876 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS) {
1877 return;
1878 }
1879
1880 PW_TEST_ASSERT_OK_AND_ASSIGN(
1881 auto view,
1882 MakeEmbossView<emboss::NumberOfCompletedPacketsEventView>(
1883 packet.GetHciSpan()));
1884 EXPECT_EQ(packet.GetHciSpan().size(), 7ul);
1885 EXPECT_EQ(view.num_handles().Read(), 1);
1886 EXPECT_EQ(view.header().event_code_enum().Read(),
1887 emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS);
1888
1889 // Should be unchanged.
1890 EXPECT_EQ(view.nocp_data()[0].connection_handle().Read(),
1891 host_capture.connection_handle);
1892 EXPECT_EQ(view.nocp_data()[0].num_completed_packets().Read(), 1);
1893 });
1894 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1895 [&controller_capture]([[maybe_unused]] H4PacketWithH4&& packet) {
1896 ++controller_capture.sends_called;
1897 });
1898
1899 ProxyHost proxy = ProxyHost(
1900 std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
1901 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1902 EXPECT_EQ(host_capture.sends_called, 1);
1903
1904 std::array<uint8_t, 1> attribute_value = {0};
1905 EXPECT_TRUE(proxy
1906 .SendGattNotify(controller_capture.connection_handle,
1907 1,
1908 pw::span(attribute_value))
1909 .ok());
1910 EXPECT_EQ(controller_capture.sends_called, 1);
1911
1912 proxy.Reset();
1913
1914 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1915 // Reset should not have cleared `le_acl_credits_to_reserve`, so proxy should
1916 // still indicate the capability.
1917 EXPECT_TRUE(proxy.HasSendLeAclCapability());
1918
1919 // Re-initialize AclDataChannel with 2 credits.
1920 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 2));
1921 EXPECT_EQ(host_capture.sends_called, 2);
1922
1923 // Send ACL on random handle to expend one credit.
1924 EXPECT_TRUE(proxy.SendGattNotify(1, 1, pw::span(attribute_value)).ok());
1925 EXPECT_EQ(controller_capture.sends_called, 2);
1926 // This should have no effect, as the reset has cleared our active connection
1927 // on this handle.
1928 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
1929 proxy,
1930 FlatMap<uint16_t, uint16_t, 1>({{{host_capture.connection_handle, 1}}})));
1931 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 1);
1932 // NOCP has credits remaining so will be passed on to host.
1933 EXPECT_EQ(host_capture.sends_called, 3);
1934 }
1935
TEST(ResetTest,ProxyHandlesMultipleResets)1936 TEST(ResetTest, ProxyHandlesMultipleResets) {
1937 int sends_called = 0;
1938
1939 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
1940 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1941 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1942 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
1943 ++sends_called;
1944 });
1945
1946 ProxyHost proxy = ProxyHost(
1947 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
1948 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1949
1950 proxy.Reset();
1951 proxy.Reset();
1952
1953 std::array<uint8_t, 1> attribute_value = {0};
1954 // Validate state after double reset.
1955 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1956 EXPECT_TRUE(proxy.HasSendAclCapability());
1957 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1958 EXPECT_EQ(proxy.SendGattNotify(1, 1, pw::span(attribute_value)),
1959 PW_STATUS_OK);
1960 EXPECT_EQ(sends_called, 1);
1961
1962 proxy.Reset();
1963
1964 // Validate state after third reset.
1965 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
1966 EXPECT_TRUE(proxy.HasSendAclCapability());
1967 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
1968 EXPECT_EQ(proxy.SendGattNotify(1, 1, pw::span(attribute_value)),
1969 PW_STATUS_OK);
1970 EXPECT_EQ(sends_called, 2);
1971 }
1972
1973 // ########## MultiSendTest
1974
TEST(MultiSendTest,CanOccupyAllThenReuseEachBuffer)1975 TEST(MultiSendTest, CanOccupyAllThenReuseEachBuffer) {
1976 constexpr size_t kMaxSends = ProxyHost::GetNumSimultaneousAclSendsSupported();
1977 struct {
1978 size_t sends_called = 0;
1979 std::array<H4PacketWithH4, 2 * kMaxSends> released_packets;
1980 } capture;
1981
1982 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
1983 []([[maybe_unused]] H4PacketWithHci&& packet) {});
1984 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
1985 [&capture](H4PacketWithH4&& packet) {
1986 // Capture all packets to prevent their destruction.
1987 capture.released_packets[capture.sends_called++] = std::move(packet);
1988 });
1989
1990 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
1991 std::move(send_to_controller_fn),
1992 2 * kMaxSends);
1993 // Allow proxy to reserve enough credits to send twice the number of
1994 // simultaneous sends supported by proxy.
1995 PW_TEST_EXPECT_OK(
1996 SendLeReadBufferResponseFromController(proxy, 2 * kMaxSends));
1997
1998 std::array<uint8_t, 1> attribute_value = {0xF};
1999 // Occupy all send buffers.
2000 for (size_t i = 0; i < kMaxSends; ++i) {
2001 EXPECT_TRUE(proxy.SendGattNotify(123, 345, pw::span(attribute_value)).ok());
2002 }
2003 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), kMaxSends);
2004 EXPECT_EQ(proxy.SendGattNotify(123, 345, pw::span(attribute_value)),
2005 PW_STATUS_UNAVAILABLE);
2006
2007 // Confirm we can release and reoccupy each buffer slot.
2008 for (size_t i = 0; i < kMaxSends; ++i) {
2009 capture.released_packets[i].~H4PacketWithH4();
2010 EXPECT_TRUE(proxy.SendGattNotify(123, 345, pw::span(attribute_value)).ok());
2011 EXPECT_EQ(proxy.SendGattNotify(123, 345, pw::span(attribute_value)),
2012 PW_STATUS_UNAVAILABLE);
2013 }
2014 EXPECT_EQ(capture.sends_called, 2 * kMaxSends);
2015
2016 // If captured packets are not reset here, they may destruct after the proxy
2017 // and lead to a crash when trying to lock the proxy's destructed mutex.
2018 for (auto& packet : capture.released_packets) {
2019 packet.ResetAndReturnReleaseFn();
2020 }
2021 }
2022
TEST(MultiSendTest,CanRepeatedlyReuseOneBuffer)2023 TEST(MultiSendTest, CanRepeatedlyReuseOneBuffer) {
2024 constexpr size_t kMaxSends = ProxyHost::GetNumSimultaneousAclSendsSupported();
2025 struct {
2026 size_t sends_called = 0;
2027 std::array<H4PacketWithH4, kMaxSends> released_packets;
2028 } capture;
2029
2030 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2031 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2032 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2033 [&capture](H4PacketWithH4&& packet) {
2034 // Capture first kMaxSends packets linearly.
2035 if (capture.sends_called < capture.released_packets.size()) {
2036 capture.released_packets[capture.sends_called] = std::move(packet);
2037 } else {
2038 // Reuse only first packet slot after kMaxSends.
2039 capture.released_packets[0] = std::move(packet);
2040 }
2041 ++capture.sends_called;
2042 });
2043
2044 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2045 std::move(send_to_controller_fn),
2046 2 * kMaxSends);
2047 PW_TEST_EXPECT_OK(
2048 SendLeReadBufferResponseFromController(proxy, 2 * kMaxSends));
2049
2050 std::array<uint8_t, 1> attribute_value = {0xF};
2051 // Occupy all send buffers.
2052 for (size_t i = 0; i < kMaxSends; ++i) {
2053 EXPECT_TRUE(proxy.SendGattNotify(123, 345, pw::span(attribute_value)).ok());
2054 }
2055
2056 // Repeatedly free and reoccupy first buffer.
2057 for (size_t i = 0; i < kMaxSends; ++i) {
2058 capture.released_packets[0].~H4PacketWithH4();
2059 EXPECT_TRUE(proxy.SendGattNotify(123, 345, pw::span(attribute_value)).ok());
2060 EXPECT_EQ(proxy.SendGattNotify(123, 345, pw::span(attribute_value)),
2061 PW_STATUS_UNAVAILABLE);
2062 }
2063 EXPECT_EQ(capture.sends_called, 2 * kMaxSends);
2064
2065 // If captured packets are not reset here, they may destruct after the proxy
2066 // and lead to a crash when trying to lock the proxy's destructed mutex.
2067 for (auto& packet : capture.released_packets) {
2068 packet.ResetAndReturnReleaseFn();
2069 }
2070 }
2071
TEST(MultiSendTest,CanSendOverManyDifferentConnections)2072 TEST(MultiSendTest, CanSendOverManyDifferentConnections) {
2073 std::array<uint8_t, 1> attribute_value = {0xF};
2074 struct {
2075 uint16_t sends_called = 0;
2076 } capture;
2077
2078 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2079 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2080 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2081 [&capture]([[maybe_unused]] H4PacketWithH4&& packet) {
2082 ++capture.sends_called;
2083 });
2084
2085 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2086 std::move(send_to_controller_fn),
2087 ProxyHost::GetMaxNumLeAclConnections());
2088 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
2089 proxy, ProxyHost::GetMaxNumLeAclConnections()));
2090
2091 for (uint16_t send = 1; send <= ProxyHost::GetMaxNumLeAclConnections();
2092 send++) {
2093 // Use current send count as the connection handle.
2094 uint16_t conn_handle = send;
2095 EXPECT_TRUE(
2096 proxy.SendGattNotify(conn_handle, 345, pw::span(attribute_value)).ok());
2097 EXPECT_EQ(capture.sends_called, send);
2098 }
2099 }
2100
TEST(MultiSendTest,AttemptToSendOverMaxConnectionsFails)2101 TEST(MultiSendTest, AttemptToSendOverMaxConnectionsFails) {
2102 constexpr uint16_t kSends = kMaxProxyActiveConnections + 1;
2103 std::array<uint8_t, 1> attribute_value = {0xF};
2104 struct {
2105 uint16_t sends_called = 0;
2106 } capture;
2107
2108 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2109 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2110 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2111 [&capture]([[maybe_unused]] H4PacketWithH4&& packet) {
2112 ++capture.sends_called;
2113 });
2114
2115 ProxyHost proxy = ProxyHost(
2116 std::move(send_to_host_fn), std::move(send_to_controller_fn), kSends);
2117 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, kSends));
2118
2119 for (uint16_t send = 1; send <= kMaxProxyActiveConnections; send++) {
2120 // Use current send count as the connection handle.
2121 uint16_t conn_handle = send;
2122 EXPECT_TRUE(
2123 proxy.SendGattNotify(conn_handle, 345, pw::span(attribute_value)).ok());
2124 EXPECT_EQ(capture.sends_called, send);
2125 }
2126
2127 // Last one should fail
2128 uint16_t conn_handle = kSends;
2129 EXPECT_FALSE(
2130 proxy.SendGattNotify(conn_handle, 345, pw::span(attribute_value)).ok());
2131 EXPECT_EQ(capture.sends_called, kMaxProxyActiveConnections);
2132 }
2133
TEST(MultiSendTest,ResetClearsBuffOccupiedFlags)2134 TEST(MultiSendTest, ResetClearsBuffOccupiedFlags) {
2135 constexpr size_t kMaxSends = ProxyHost::GetNumSimultaneousAclSendsSupported();
2136 struct {
2137 size_t sends_called = 0;
2138 std::array<H4PacketWithH4, 2 * kMaxSends> released_packets;
2139 } capture;
2140
2141 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2142 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2143 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2144 [&capture](H4PacketWithH4&& packet) {
2145 // Capture all packets to prevent their destruction.
2146 capture.released_packets[capture.sends_called++] = std::move(packet);
2147 });
2148
2149 ProxyHost proxy = ProxyHost(
2150 std::move(send_to_host_fn), std::move(send_to_controller_fn), kMaxSends);
2151 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, kMaxSends));
2152
2153 std::array<uint8_t, 1> attribute_value = {0xF};
2154 // Occupy all send buffers.
2155 for (size_t i = 0; i < kMaxSends; ++i) {
2156 EXPECT_TRUE(proxy.SendGattNotify(123, 345, pw::span(attribute_value)).ok());
2157 }
2158
2159 proxy.Reset();
2160 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, kMaxSends));
2161
2162 // Although sent packets have not been released, proxy.Reset() should have
2163 // marked all buffers as unoccupied.
2164 for (size_t i = 0; i < kMaxSends; ++i) {
2165 EXPECT_TRUE(proxy.SendGattNotify(123, 345, pw::span(attribute_value)).ok());
2166 }
2167 EXPECT_EQ(capture.sends_called, 2 * kMaxSends);
2168
2169 // If captured packets are not reset here, they may destruct after the proxy
2170 // and lead to a crash when trying to lock the proxy's destructed mutex.
2171 for (auto& packet : capture.released_packets) {
2172 packet.ResetAndReturnReleaseFn();
2173 }
2174 }
2175
2176 // ########## BasicL2capChannelTest
2177
TEST(BasicL2capChannelTest,BasicWrite)2178 TEST(BasicL2capChannelTest, BasicWrite) {
2179 struct {
2180 int sends_called = 0;
2181 // First four bits 0x0 encode PB & BC flags
2182 uint16_t handle = 0x0ACB;
2183 // Length of L2CAP PDU
2184 uint16_t acl_data_total_length = 0x0007;
2185 // L2CAP header PDU length field
2186 uint16_t pdu_length = 0x0003;
2187 // Random CID
2188 uint16_t channel_id = 0x1234;
2189 // L2CAP information payload
2190 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
2191
2192 // Built from the preceding values in little endian order (except payload in
2193 // big endian).
2194 std::array<uint8_t, 11> expected_hci_packet = {
2195 0xCB, 0x0A, 0x07, 0x00, 0x03, 0x00, 0x34, 0x12, 0xAB, 0xCD, 0xEF};
2196 } capture;
2197
2198 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2199 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2200 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2201 [&capture](H4PacketWithH4&& packet) {
2202 ++capture.sends_called;
2203 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
2204 EXPECT_EQ(packet.GetHciSpan().size(),
2205 capture.expected_hci_packet.size());
2206 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
2207 packet.GetHciSpan().end(),
2208 capture.expected_hci_packet.begin(),
2209 capture.expected_hci_packet.end()));
2210 PW_TEST_ASSERT_OK_AND_ASSIGN(
2211 auto acl,
2212 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
2213 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
2214 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
2215 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
2216 EXPECT_EQ(acl.header().broadcast_flag().Read(),
2217 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
2218 EXPECT_EQ(acl.data_total_length().Read(),
2219 capture.acl_data_total_length);
2220 emboss::BFrameView bframe = emboss::MakeBFrameView(
2221 acl.payload().BackingStorage().data(), acl.SizeInBytes());
2222 EXPECT_EQ(bframe.pdu_length().Read(), capture.pdu_length);
2223 EXPECT_EQ(bframe.channel_id().Read(), capture.channel_id);
2224 for (size_t i = 0; i < 3; ++i) {
2225 EXPECT_EQ(bframe.payload()[i].Read(), capture.payload[i]);
2226 }
2227 });
2228
2229 ProxyHost proxy = ProxyHost(
2230 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
2231 // Allow proxy to reserve 1 LE credit.
2232 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2233
2234 PW_TEST_ASSERT_OK_AND_ASSIGN(
2235 BasicL2capChannel channel,
2236 proxy.AcquireBasicL2capChannel(/*connection_handle=*/capture.handle,
2237 /*local_cid=*/0x123,
2238 /*remote_cid=*/capture.channel_id,
2239 /*transport=*/AclTransportType::kLe,
2240 /*payload_from_controller_fn=*/nullptr));
2241
2242 EXPECT_EQ(channel.Write(capture.payload), PW_STATUS_OK);
2243 EXPECT_EQ(capture.sends_called, 1);
2244 }
2245
TEST(BasicL2capChannelTest,ErrorOnWriteTooLarge)2246 TEST(BasicL2capChannelTest, ErrorOnWriteTooLarge) {
2247 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2248 [](H4PacketWithHci&&) {});
2249 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2250 [](H4PacketWithH4&&) { FAIL(); });
2251
2252 ProxyHost proxy = ProxyHost(
2253 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
2254 // Allow proxy to reserve 1 credit.
2255 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
2256
2257 std::array<uint8_t,
2258 ProxyHost::GetMaxAclSendSize() -
2259 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() -
2260 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + 1>
2261 hci_arr;
2262 PW_TEST_ASSERT_OK_AND_ASSIGN(
2263 BasicL2capChannel channel,
2264 proxy.AcquireBasicL2capChannel(/*connection_handle=*/0x123,
2265 /*local_cid=*/0x123,
2266 /*remote_cid=*/0x123,
2267 /*transport=*/AclTransportType::kLe,
2268 /*payload_from_controller_fn=*/nullptr));
2269
2270 EXPECT_EQ(channel.Write(span(hci_arr)), PW_STATUS_INVALID_ARGUMENT);
2271 }
2272
TEST(BasicL2capChannelTest,CannotCreateChannelWithInvalidArgs)2273 TEST(BasicL2capChannelTest, CannotCreateChannelWithInvalidArgs) {
2274 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2275 [](H4PacketWithHci&&) {});
2276 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2277 [](H4PacketWithH4&&) {});
2278
2279 ProxyHost proxy = ProxyHost(
2280 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2281
2282 // Connection handle too large by 1.
2283 EXPECT_EQ(
2284 proxy
2285 .AcquireBasicL2capChannel(/*connection_handle=*/0x0FFF,
2286 /*local_cid=*/0x123,
2287 /*remote_cid=*/0x123,
2288 /*transport=*/AclTransportType::kLe,
2289 /*payload_from_controller_fn=*/nullptr)
2290 .status(),
2291 PW_STATUS_INVALID_ARGUMENT);
2292
2293 // Local CID invalid (0).
2294 EXPECT_EQ(
2295 proxy
2296 .AcquireBasicL2capChannel(/*connection_handle=*/0x123,
2297 /*local_cid=*/0,
2298 /*remote_cid=*/0x123,
2299 /*transport=*/AclTransportType::kLe,
2300 /*payload_from_controller_fn=*/nullptr)
2301 .status(),
2302 PW_STATUS_INVALID_ARGUMENT);
2303 }
2304
TEST(BasicL2capChannelTest,BasicRead)2305 TEST(BasicL2capChannelTest, BasicRead) {
2306 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2307 [](H4PacketWithHci&&) {});
2308 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2309 [](H4PacketWithH4&&) {});
2310 ProxyHost proxy = ProxyHost(
2311 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2312
2313 struct {
2314 int sends_called = 0;
2315 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
2316 } capture;
2317
2318 uint16_t handle = 334;
2319 uint16_t local_cid = 443;
2320 PW_TEST_ASSERT_OK_AND_ASSIGN(
2321 BasicL2capChannel channel,
2322 proxy.AcquireBasicL2capChannel(
2323 /*connection_handle=*/handle,
2324 /*local_cid=*/local_cid,
2325 /*remote_cid=*/0x123,
2326 /*transport=*/AclTransportType::kLe,
2327 /*payload_from_controller_fn=*/
2328 [&capture](pw::span<uint8_t> payload) {
2329 ++capture.sends_called;
2330 EXPECT_TRUE(std::equal(payload.begin(),
2331 payload.end(),
2332 capture.expected_payload.begin(),
2333 capture.expected_payload.end()));
2334 }));
2335
2336 std::array<uint8_t,
2337 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2338 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2339 capture.expected_payload.size()>
2340 hci_arr;
2341 hci_arr.fill(0);
2342 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2343
2344 Result<emboss::AclDataFrameWriter> acl =
2345 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2346 acl->header().handle().Write(handle);
2347 acl->data_total_length().Write(
2348 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
2349 capture.expected_payload.size());
2350
2351 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
2352 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
2353 bframe.pdu_length().Write(capture.expected_payload.size());
2354 bframe.channel_id().Write(local_cid);
2355 std::copy(capture.expected_payload.begin(),
2356 capture.expected_payload.end(),
2357 hci_arr.begin() +
2358 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2359 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
2360
2361 // Send ACL data packet destined for the CoC we registered.
2362 proxy.HandleH4HciFromController(std::move(h4_packet));
2363
2364 EXPECT_EQ(capture.sends_called, 1);
2365 }
2366
2367 // ########## L2capCocTest
2368
TEST(L2capCocTest,CannotCreateChannelWithInvalidArgs)2369 TEST(L2capCocTest, CannotCreateChannelWithInvalidArgs) {
2370 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2371 [](H4PacketWithHci&&) {});
2372 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2373 [](H4PacketWithH4&&) {});
2374
2375 ProxyHost proxy = ProxyHost(
2376 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2377
2378 // Connection handle too large by 1.
2379 EXPECT_EQ(
2380 proxy
2381 .AcquireL2capCoc(
2382 /*connection_handle=*/0x0FFF,
2383 /*rx_config=*/
2384 {.cid = 0x123, .mtu = 0x123, .mps = 0x123, .credits = 0x123},
2385 /*tx_config=*/
2386 {.cid = 0x123, .mtu = 0x123, .mps = 0x123, .credits = 0x123},
2387 /*receive_fn=*/nullptr,
2388 /*event_fn=*/nullptr)
2389 .status(),
2390 PW_STATUS_INVALID_ARGUMENT);
2391
2392 // Local CID invalid (0).
2393 EXPECT_EQ(
2394 proxy
2395 .AcquireL2capCoc(
2396 /*connection_handle=*/0x123,
2397 /*rx_config=*/
2398 {.cid = 0, .mtu = 0x123, .mps = 0x123, .credits = 0x123},
2399 /*tx_config=*/
2400 {.cid = 0x123, .mtu = 0x123, .mps = 0x123, .credits = 0x123},
2401 /*receive_fn=*/nullptr,
2402 /*event_fn=*/nullptr)
2403 .status(),
2404 PW_STATUS_INVALID_ARGUMENT);
2405
2406 // Remote CID invalid (0).
2407 EXPECT_EQ(
2408 proxy
2409 .AcquireL2capCoc(
2410 /*connection_handle=*/0x123,
2411 /*rx_config=*/
2412 {.cid = 0x123, .mtu = 0x123, .mps = 0x123, .credits = 0x123},
2413 /*tx_config=*/
2414 {.cid = 0, .mtu = 0x123, .mps = 0x123, .credits = 0x123},
2415 /*receive_fn=*/nullptr,
2416 /*event_fn=*/nullptr)
2417 .status(),
2418 PW_STATUS_INVALID_ARGUMENT);
2419 }
2420
2421 // ########## L2capCocWriteTest
2422
2423 // Size of sdu_length field in first K-frames.
2424 constexpr uint8_t kSduLengthFieldSize = 2;
2425 // Size of a K-Frame over Acl packet with no payload.
2426 constexpr uint8_t kFirstKFrameOverAclMinSize =
2427 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
2428 emboss::FirstKFrame::MinSizeInBytes();
2429
TEST(L2capCocWriteTest,BasicWrite)2430 TEST(L2capCocWriteTest, BasicWrite) {
2431 struct {
2432 int sends_called = 0;
2433 // First four bits 0x0 encode PB & BC flags
2434 uint16_t handle = 0x0ACB;
2435 // Length of L2CAP PDU
2436 uint16_t acl_data_total_length = 0x0009;
2437 // L2CAP header PDU length field
2438 uint16_t pdu_length = 0x0005;
2439 // Random CID
2440 uint16_t channel_id = 0x1234;
2441 // Length of L2CAP SDU
2442 uint16_t sdu_length = 0x0003;
2443 // L2CAP information payload
2444 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
2445
2446 // Built from the preceding values in little endian order (except payload in
2447 // big endian).
2448 std::array<uint8_t, 13> expected_hci_packet = {0xCB,
2449 0x0A,
2450 0x09,
2451 0x00,
2452 0x05,
2453 0x00,
2454 0x34,
2455 0x12,
2456 0x03,
2457 0x00,
2458 0xAB,
2459 0xCD,
2460 0xEF};
2461 } capture;
2462
2463 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2464 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2465 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2466 [&capture](H4PacketWithH4&& packet) {
2467 ++capture.sends_called;
2468 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
2469 EXPECT_EQ(packet.GetHciSpan().size(),
2470 capture.expected_hci_packet.size());
2471 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
2472 packet.GetHciSpan().end(),
2473 capture.expected_hci_packet.begin(),
2474 capture.expected_hci_packet.end()));
2475 PW_TEST_ASSERT_OK_AND_ASSIGN(
2476 auto acl,
2477 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
2478 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
2479 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
2480 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
2481 EXPECT_EQ(acl.header().broadcast_flag().Read(),
2482 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
2483 EXPECT_EQ(acl.data_total_length().Read(),
2484 capture.acl_data_total_length);
2485 emboss::FirstKFrameView kframe = emboss::MakeFirstKFrameView(
2486 acl.payload().BackingStorage().data(), acl.SizeInBytes());
2487 EXPECT_EQ(kframe.pdu_length().Read(), capture.pdu_length);
2488 EXPECT_EQ(kframe.channel_id().Read(), capture.channel_id);
2489 EXPECT_EQ(kframe.sdu_length().Read(), capture.sdu_length);
2490 for (size_t i = 0; i < 3; ++i) {
2491 EXPECT_EQ(kframe.payload()[i].Read(), capture.payload[i]);
2492 }
2493 });
2494
2495 ProxyHost proxy = ProxyHost(
2496 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
2497 // Allow proxy to reserve 1 credit.
2498 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2499
2500 L2capCoc channel = BuildCoc(proxy,
2501 CocParameters{.handle = capture.handle,
2502 .remote_cid = capture.channel_id});
2503 EXPECT_EQ(channel.Write(capture.payload), PW_STATUS_OK);
2504 EXPECT_EQ(capture.sends_called, 1);
2505 }
2506
TEST(L2capCocWriteTest,ErrorOnWriteToStoppedChannel)2507 TEST(L2capCocWriteTest, ErrorOnWriteToStoppedChannel) {
2508 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2509 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2510 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2511 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2512 ProxyHost proxy = ProxyHost(
2513 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
2514 // Allow proxy to reserve 1 credit.
2515 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2516
2517 L2capCoc channel = BuildCoc(
2518 proxy,
2519 CocParameters{
2520 .handle = 123,
2521 .tx_credits = 1,
2522 .event_fn = []([[maybe_unused]] L2capCoc::Event event) { FAIL(); }});
2523
2524 EXPECT_EQ(channel.Stop(), PW_STATUS_OK);
2525 EXPECT_EQ(channel.Write({}), PW_STATUS_FAILED_PRECONDITION);
2526 }
2527
TEST(L2capCocWriteTest,TooLargeWritesFail)2528 TEST(L2capCocWriteTest, TooLargeWritesFail) {
2529 int sends_called = 0;
2530
2531 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2532 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2533 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2534 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
2535 ++sends_called;
2536 });
2537
2538 ProxyHost proxy = ProxyHost(
2539 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
2540 // Allow proxy to reserve 1 credit.
2541 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
2542
2543 // Payload size exceeds MTU.
2544 L2capCoc small_mtu_channel = BuildCoc(proxy, CocParameters{.tx_mtu = 1});
2545 std::array<uint8_t, 24> payload;
2546 EXPECT_EQ(small_mtu_channel.Write(payload), PW_STATUS_INVALID_ARGUMENT);
2547
2548 // Payload size exceeds MPS.
2549 L2capCoc small_mps_channel = BuildCoc(proxy, CocParameters{.tx_mps = 23});
2550 EXPECT_EQ(small_mps_channel.Write(payload), PW_STATUS_INVALID_ARGUMENT);
2551
2552 // Payload size exceeds max allowable based on H4 buffer size.
2553 std::array<uint8_t,
2554 proxy.GetMaxAclSendSize() - kFirstKFrameOverAclMinSize + 1>
2555 payload_one_byte_too_large;
2556 L2capCoc channel = BuildCoc(proxy, {});
2557 EXPECT_EQ(channel.Write(payload_one_byte_too_large),
2558 PW_STATUS_INVALID_ARGUMENT);
2559
2560 EXPECT_EQ(sends_called, 0);
2561 }
2562
TEST(L2capCocWriteTest,MultipleWritesSameChannel)2563 TEST(L2capCocWriteTest, MultipleWritesSameChannel) {
2564 struct {
2565 int sends_called = 0;
2566 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
2567 } capture;
2568
2569 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2570 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2571 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2572 [&capture](H4PacketWithH4&& packet) {
2573 ++capture.sends_called;
2574 PW_TEST_ASSERT_OK_AND_ASSIGN(
2575 auto acl,
2576 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
2577 emboss::FirstKFrameView kframe = emboss::MakeFirstKFrameView(
2578 acl.payload().BackingStorage().data(), acl.SizeInBytes());
2579 for (size_t i = 0; i < 3; ++i) {
2580 EXPECT_EQ(kframe.payload()[i].Read(), capture.payload[i]);
2581 }
2582 });
2583
2584 uint16_t num_writes = 5;
2585 ProxyHost proxy = ProxyHost(
2586 std::move(send_to_host_fn), std::move(send_to_controller_fn), num_writes);
2587 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
2588 proxy,
2589 /*num_credits_to_reserve=*/num_writes));
2590
2591 L2capCoc channel = BuildCoc(proxy, CocParameters{.tx_credits = num_writes});
2592 for (int i = 0; i < num_writes; ++i) {
2593 EXPECT_EQ(channel.Write(capture.payload), PW_STATUS_OK);
2594 std::for_each(capture.payload.begin(),
2595 capture.payload.end(),
2596 [](uint8_t& byte) { ++byte; });
2597 }
2598
2599 EXPECT_EQ(capture.sends_called, num_writes);
2600 }
2601
TEST(L2capCocWriteTest,MultipleWritesMultipleChannels)2602 TEST(L2capCocWriteTest, MultipleWritesMultipleChannels) {
2603 struct {
2604 int sends_called = 0;
2605 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
2606 } capture;
2607
2608 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2609 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2610 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2611 [&capture](H4PacketWithH4&& packet) {
2612 ++capture.sends_called;
2613 PW_TEST_ASSERT_OK_AND_ASSIGN(
2614 auto acl,
2615 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
2616 emboss::FirstKFrameView kframe = emboss::MakeFirstKFrameView(
2617 acl.payload().BackingStorage().data(), acl.SizeInBytes());
2618 for (size_t i = 0; i < 3; ++i) {
2619 EXPECT_EQ(kframe.payload()[i].Read(), capture.payload[i]);
2620 }
2621 });
2622
2623 constexpr uint16_t kNumChannels = 5;
2624 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2625 std::move(send_to_controller_fn),
2626 kNumChannels);
2627 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(
2628 proxy,
2629 /*num_credits_to_reserve=*/kNumChannels));
2630
2631 uint16_t remote_cid = 123;
2632 std::array<L2capCoc, kNumChannels> channels{
2633 BuildCoc(proxy, CocParameters{.remote_cid = remote_cid}),
2634 BuildCoc(
2635 proxy,
2636 CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 1)}),
2637 BuildCoc(
2638 proxy,
2639 CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 2)}),
2640 BuildCoc(
2641 proxy,
2642 CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 3)}),
2643 BuildCoc(
2644 proxy,
2645 CocParameters{.remote_cid = static_cast<uint16_t>(remote_cid + 4)}),
2646 };
2647
2648 for (int i = 0; i < kNumChannels; ++i) {
2649 EXPECT_EQ(channels[i].Write(capture.payload), PW_STATUS_OK);
2650 std::for_each(capture.payload.begin(),
2651 capture.payload.end(),
2652 [](uint8_t& byte) { ++byte; });
2653 }
2654
2655 EXPECT_EQ(capture.sends_called, kNumChannels);
2656 }
2657
2658 // ########## L2capCocReadTest
2659
TEST(L2capCocReadTest,BasicRead)2660 TEST(L2capCocReadTest, BasicRead) {
2661 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2662 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2663 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2664 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2665 ProxyHost proxy = ProxyHost(
2666 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2667
2668 struct {
2669 int sends_called = 0;
2670 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
2671 } capture;
2672
2673 uint16_t handle = 123;
2674 uint16_t local_cid = 234;
2675 L2capCoc channel = BuildCoc(
2676 proxy,
2677 CocParameters{.handle = handle,
2678 .local_cid = local_cid,
2679 .receive_fn = [&capture](pw::span<uint8_t> payload) {
2680 ++capture.sends_called;
2681 EXPECT_TRUE(std::equal(payload.begin(),
2682 payload.end(),
2683 capture.expected_payload.begin(),
2684 capture.expected_payload.end()));
2685 }});
2686
2687 std::array<uint8_t,
2688 kFirstKFrameOverAclMinSize + capture.expected_payload.size()>
2689 hci_arr;
2690 hci_arr.fill(0);
2691 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2692
2693 Result<emboss::AclDataFrameWriter> acl =
2694 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2695 acl->header().handle().Write(handle);
2696 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
2697 capture.expected_payload.size());
2698
2699 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
2700 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
2701 kframe.pdu_length().Write(kSduLengthFieldSize +
2702 capture.expected_payload.size());
2703 kframe.channel_id().Write(local_cid);
2704 kframe.sdu_length().Write(capture.expected_payload.size());
2705 std::copy(capture.expected_payload.begin(),
2706 capture.expected_payload.end(),
2707 hci_arr.begin() + kFirstKFrameOverAclMinSize);
2708
2709 // Send ACL data packet destined for the CoC we registered.
2710 proxy.HandleH4HciFromController(std::move(h4_packet));
2711
2712 EXPECT_EQ(capture.sends_called, 1);
2713 }
2714
TEST(L2capCocReadTest,ChannelHandlesReadWithNullReceiveFn)2715 TEST(L2capCocReadTest, ChannelHandlesReadWithNullReceiveFn) {
2716 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2717 []([[maybe_unused]] H4PacketWithHci&& packet) { FAIL(); });
2718 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2719 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2720 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2721 std::move(send_to_controller_fn),
2722 /*le_acl_credits_to_reserve=*/0,
2723 /*br_edr_acl_credits_to_reserve=*/0);
2724
2725 uint16_t handle = 123;
2726 uint16_t local_cid = 234;
2727 L2capCoc channel = BuildCoc(
2728 proxy,
2729 CocParameters{
2730 .handle = handle,
2731 .local_cid = local_cid,
2732 .rx_credits = 1,
2733 .event_fn = []([[maybe_unused]] L2capCoc::Event event) { FAIL(); }});
2734
2735 std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
2736 hci_arr.fill(0);
2737 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2738
2739 Result<emboss::AclDataFrameWriter> acl =
2740 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2741 acl->header().handle().Write(handle);
2742 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
2743
2744 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
2745 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
2746 kframe.pdu_length().Write(kSduLengthFieldSize);
2747 kframe.channel_id().Write(local_cid);
2748 kframe.sdu_length().Write(0);
2749
2750 proxy.HandleH4HciFromController(std::move(h4_packet));
2751 }
2752
TEST(L2capCocReadTest,ErrorOnRxToStoppedChannel)2753 TEST(L2capCocReadTest, ErrorOnRxToStoppedChannel) {
2754 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2755 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2756 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
2757 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2758 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
2759 std::move(send_to_controller_fn),
2760 /*le_acl_credits_to_reserve=*/0,
2761 /*br_edr_acl_credits_to_reserve*/ 0);
2762
2763 int events_received = 0;
2764 uint16_t num_invalid_rx = 3;
2765 uint16_t handle = 123;
2766 uint16_t local_cid = 234;
2767 L2capCoc channel = BuildCoc(
2768 proxy,
2769 CocParameters{
2770 .handle = handle,
2771 .local_cid = local_cid,
2772 .rx_credits = num_invalid_rx,
2773 .receive_fn =
2774 []([[maybe_unused]] pw::span<uint8_t> payload) { FAIL(); },
2775 .event_fn =
2776 [&events_received](L2capCoc::Event event) {
2777 ++events_received;
2778 EXPECT_EQ(event, L2capCoc::Event::kRxWhileStopped);
2779 }});
2780
2781 std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
2782 hci_arr.fill(0);
2783
2784 Result<emboss::AclDataFrameWriter> acl =
2785 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2786 acl->header().handle().Write(handle);
2787 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
2788
2789 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
2790 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
2791 kframe.pdu_length().Write(kSduLengthFieldSize);
2792 kframe.channel_id().Write(local_cid);
2793 kframe.sdu_length().Write(0);
2794
2795 EXPECT_EQ(channel.Stop(), PW_STATUS_OK);
2796 for (int i = 0; i < num_invalid_rx; ++i) {
2797 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2798 proxy.HandleH4HciFromController(std::move(h4_packet));
2799 }
2800 EXPECT_EQ(events_received, num_invalid_rx);
2801 }
2802
TEST(L2capCocReadTest,TooShortAclPassedToHost)2803 TEST(L2capCocReadTest, TooShortAclPassedToHost) {
2804 int sends_called = 0;
2805 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2806 [&sends_called]([[maybe_unused]] H4PacketWithHci&& packet) {
2807 ++sends_called;
2808 });
2809 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2810 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2811 ProxyHost proxy = ProxyHost(
2812 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2813
2814 uint16_t handle = 123;
2815 uint16_t local_cid = 234;
2816 L2capCoc channel = BuildCoc(
2817 proxy,
2818 CocParameters{
2819 .handle = handle,
2820 .local_cid = local_cid,
2821 .receive_fn = []([[maybe_unused]] pw::span<uint8_t> payload) {
2822 FAIL();
2823 }});
2824
2825 std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
2826 hci_arr.fill(0);
2827 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2828
2829 Result<emboss::AclDataFrameWriter> acl =
2830 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2831 acl->header().handle().Write(handle);
2832 // Write size larger than buffer size.
2833 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() + 5);
2834
2835 proxy.HandleH4HciFromController(std::move(h4_packet));
2836
2837 EXPECT_EQ(sends_called, 1);
2838 }
2839
TEST(L2capCocReadTest,ChannelClosedWithErrorIfMtuExceeded)2840 TEST(L2capCocReadTest, ChannelClosedWithErrorIfMtuExceeded) {
2841 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2842 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2843 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2844 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2845 ProxyHost proxy = ProxyHost(
2846 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2847
2848 uint16_t handle = 123;
2849 uint16_t local_cid = 234;
2850 constexpr uint16_t kRxMtu = 5;
2851 int events_received = 0;
2852 L2capCoc channel = BuildCoc(
2853 proxy,
2854 CocParameters{
2855 .handle = handle,
2856 .local_cid = local_cid,
2857 .rx_mtu = kRxMtu,
2858 .receive_fn =
2859 []([[maybe_unused]] pw::span<uint8_t> payload) { FAIL(); },
2860 .event_fn =
2861 [&events_received](L2capCoc::Event event) {
2862 ++events_received;
2863 EXPECT_EQ(event, L2capCoc::Event::kRxInvalid);
2864 }});
2865
2866 constexpr uint16_t kPayloadSize = kRxMtu + 1;
2867 std::array<uint8_t, kFirstKFrameOverAclMinSize + kPayloadSize> hci_arr;
2868 hci_arr.fill(0);
2869 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2870
2871 Result<emboss::AclDataFrameWriter> acl =
2872 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2873 acl->header().handle().Write(handle);
2874 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
2875 kPayloadSize);
2876
2877 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
2878 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
2879 kframe.pdu_length().Write(kSduLengthFieldSize + kPayloadSize);
2880 kframe.channel_id().Write(local_cid);
2881 kframe.sdu_length().Write(kPayloadSize);
2882
2883 proxy.HandleH4HciFromController(std::move(h4_packet));
2884
2885 EXPECT_EQ(events_received, 1);
2886 }
2887
TEST(L2capCocReadTest,ChannelClosedWithErrorIfMpsExceeded)2888 TEST(L2capCocReadTest, ChannelClosedWithErrorIfMpsExceeded) {
2889 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2890 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2891 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2892 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2893 ProxyHost proxy = ProxyHost(
2894 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2895
2896 uint16_t handle = 123;
2897 uint16_t local_cid = 234;
2898 constexpr uint16_t kRxMps = 5;
2899 int events_received = 0;
2900 L2capCoc channel = BuildCoc(
2901 proxy,
2902 CocParameters{
2903 .handle = handle,
2904 .local_cid = local_cid,
2905 .rx_mps = kRxMps,
2906 .receive_fn =
2907 []([[maybe_unused]] pw::span<uint8_t> payload) { FAIL(); },
2908 .event_fn =
2909 [&events_received](L2capCoc::Event event) {
2910 ++events_received;
2911 EXPECT_EQ(event, L2capCoc::Event::kRxInvalid);
2912 }});
2913
2914 constexpr uint16_t kPayloadSize = kRxMps + 1;
2915 std::array<uint8_t, kFirstKFrameOverAclMinSize + kPayloadSize> hci_arr;
2916 hci_arr.fill(0);
2917 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
2918
2919 Result<emboss::AclDataFrameWriter> acl =
2920 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2921 acl->header().handle().Write(handle);
2922 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
2923 kPayloadSize);
2924
2925 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
2926 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
2927 kframe.pdu_length().Write(kSduLengthFieldSize + kPayloadSize);
2928 kframe.channel_id().Write(local_cid);
2929 kframe.sdu_length().Write(kPayloadSize);
2930
2931 proxy.HandleH4HciFromController(std::move(h4_packet));
2932
2933 EXPECT_EQ(events_received, 1);
2934 }
2935
TEST(L2capCocReadTest,ChannelClosedWithErrorIfPayloadsExceedSduLength)2936 TEST(L2capCocReadTest, ChannelClosedWithErrorIfPayloadsExceedSduLength) {
2937 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
2938 []([[maybe_unused]] H4PacketWithHci&& packet) {});
2939 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
2940 []([[maybe_unused]] H4PacketWithH4&& packet) {});
2941 ProxyHost proxy = ProxyHost(
2942 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
2943
2944 uint16_t handle = 123;
2945 uint16_t local_cid = 234;
2946 int events_received = 0;
2947 L2capCoc channel = BuildCoc(
2948 proxy,
2949 CocParameters{
2950 .handle = handle,
2951 .local_cid = local_cid,
2952 .receive_fn =
2953 []([[maybe_unused]] pw::span<uint8_t> payload) { FAIL(); },
2954 .event_fn =
2955 [&events_received](L2capCoc::Event event) {
2956 ++events_received;
2957 EXPECT_EQ(event, L2capCoc::Event::kRxInvalid);
2958 }});
2959
2960 constexpr uint16_t k1stPayloadSize = 1;
2961 constexpr uint16_t k2ndPayloadSize = 3;
2962 ASSERT_GT(k2ndPayloadSize, k1stPayloadSize + 1);
2963 // Indicate SDU length that does not account for the 2nd payload size.
2964 constexpr uint16_t kSduLength = k1stPayloadSize + 1;
2965
2966 std::array<uint8_t,
2967 kFirstKFrameOverAclMinSize +
2968 std::max(k1stPayloadSize, k2ndPayloadSize)>
2969 hci_arr;
2970 hci_arr.fill(0);
2971 H4PacketWithHci h4_1st_segment{
2972 emboss::H4PacketType::ACL_DATA,
2973 pw::span<uint8_t>(hci_arr.data(),
2974 kFirstKFrameOverAclMinSize + k1stPayloadSize)};
2975
2976 Result<emboss::AclDataFrameWriter> acl =
2977 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
2978 acl->header().handle().Write(handle);
2979 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
2980 k1stPayloadSize);
2981
2982 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
2983 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
2984 kframe.pdu_length().Write(kSduLengthFieldSize + k1stPayloadSize);
2985 kframe.channel_id().Write(local_cid);
2986 kframe.sdu_length().Write(kSduLength);
2987
2988 proxy.HandleH4HciFromController(std::move(h4_1st_segment));
2989
2990 // Send 2nd segment.
2991 acl->data_total_length().Write(emboss::SubsequentKFrame::MinSizeInBytes() +
2992 k2ndPayloadSize);
2993 kframe.pdu_length().Write(k2ndPayloadSize);
2994 H4PacketWithHci h4_2nd_segment{
2995 emboss::H4PacketType::ACL_DATA,
2996 pw::span<uint8_t>(hci_arr.data(),
2997 emboss::AclDataFrame::MinSizeInBytes() +
2998 emboss::SubsequentKFrame::MinSizeInBytes() +
2999 k2ndPayloadSize)};
3000
3001 proxy.HandleH4HciFromController(std::move(h4_2nd_segment));
3002
3003 EXPECT_EQ(events_received, 1);
3004 }
3005
TEST(L2capCocReadTest,NoReadOnStoppedChannel)3006 TEST(L2capCocReadTest, NoReadOnStoppedChannel) {
3007 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3008 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3009 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3010 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3011 ProxyHost proxy = ProxyHost(
3012 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3013
3014 uint16_t handle = 123;
3015 uint16_t local_cid = 234;
3016 L2capCoc channel = BuildCoc(
3017 proxy,
3018 CocParameters{
3019 .handle = handle,
3020 .local_cid = local_cid,
3021 .receive_fn = []([[maybe_unused]] pw::span<uint8_t> payload) {
3022 FAIL();
3023 }});
3024
3025 std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
3026 hci_arr.fill(0);
3027 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3028
3029 Result<emboss::AclDataFrameWriter> acl =
3030 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3031 acl->header().handle().Write(handle);
3032 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3033
3034 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3035 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3036 kframe.pdu_length().Write(kSduLengthFieldSize);
3037 kframe.channel_id().Write(local_cid);
3038
3039 EXPECT_EQ(channel.Stop(), PW_STATUS_OK);
3040 proxy.HandleH4HciFromController(std::move(h4_packet));
3041 }
3042
TEST(L2capCocReadTest,NoReadOnSameCidDifferentConnectionHandle)3043 TEST(L2capCocReadTest, NoReadOnSameCidDifferentConnectionHandle) {
3044 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3045 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3046 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3047 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3048 ProxyHost proxy = ProxyHost(
3049 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3050
3051 uint16_t local_cid = 234;
3052 L2capCoc channel = BuildCoc(
3053 proxy,
3054 CocParameters{
3055 .local_cid = local_cid,
3056 .receive_fn = []([[maybe_unused]] pw::span<uint8_t> payload) {
3057 FAIL();
3058 }});
3059
3060 std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
3061 hci_arr.fill(0);
3062 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3063
3064 Result<emboss::AclDataFrameWriter> acl =
3065 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3066 acl->header().handle().Write(444);
3067 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3068
3069 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3070 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3071 kframe.pdu_length().Write(kSduLengthFieldSize);
3072 kframe.channel_id().Write(local_cid);
3073
3074 proxy.HandleH4HciFromController(std::move(h4_packet));
3075 }
3076
TEST(L2capCocReadTest,MultipleReadsSameChannel)3077 TEST(L2capCocReadTest, MultipleReadsSameChannel) {
3078 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3079 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3080 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3081 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3082 ProxyHost proxy = ProxyHost(
3083 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3084
3085 struct {
3086 int sends_called = 0;
3087 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
3088 } capture;
3089
3090 uint16_t handle = 123;
3091 uint16_t local_cid = 234;
3092 L2capCoc channel = BuildCoc(
3093 proxy,
3094 CocParameters{.handle = handle,
3095 .local_cid = local_cid,
3096 .receive_fn = [&capture](pw::span<uint8_t> payload) {
3097 ++capture.sends_called;
3098 EXPECT_TRUE(std::equal(payload.begin(),
3099 payload.end(),
3100 capture.payload.begin(),
3101 capture.payload.end()));
3102 }});
3103
3104 std::array<uint8_t, kFirstKFrameOverAclMinSize + capture.payload.size()>
3105 hci_arr;
3106 hci_arr.fill(0);
3107
3108 Result<emboss::AclDataFrameWriter> acl =
3109 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3110 acl->header().handle().Write(handle);
3111 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
3112 capture.payload.size());
3113
3114 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3115 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3116 kframe.pdu_length().Write(capture.payload.size() + kSduLengthFieldSize);
3117 kframe.channel_id().Write(local_cid);
3118 kframe.sdu_length().Write(capture.payload.size());
3119
3120 int num_reads = 10;
3121 for (int i = 0; i < num_reads; ++i) {
3122 std::copy(capture.payload.begin(),
3123 capture.payload.end(),
3124 hci_arr.begin() + kFirstKFrameOverAclMinSize);
3125
3126 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3127 proxy.HandleH4HciFromController(std::move(h4_packet));
3128
3129 std::for_each(capture.payload.begin(),
3130 capture.payload.end(),
3131 [](uint8_t& byte) { ++byte; });
3132 }
3133
3134 EXPECT_EQ(capture.sends_called, num_reads);
3135 }
3136
TEST(L2capCocReadTest,MultipleReadsMultipleChannels)3137 TEST(L2capCocReadTest, MultipleReadsMultipleChannels) {
3138 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3139 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3140 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3141 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3142 ProxyHost proxy = ProxyHost(
3143 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3144
3145 struct {
3146 int sends_called = 0;
3147 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
3148 } capture;
3149
3150 constexpr int kNumChannels = 5;
3151 uint16_t local_cid = 123;
3152 uint16_t handle = 456;
3153 auto receive_fn = [&capture](pw::span<uint8_t> payload) {
3154 ++capture.sends_called;
3155 EXPECT_TRUE(std::equal(payload.begin(),
3156 payload.end(),
3157 capture.payload.begin(),
3158 capture.payload.end()));
3159 };
3160 std::array<L2capCoc, kNumChannels> channels{
3161 BuildCoc(proxy,
3162 CocParameters{.handle = handle,
3163 .local_cid = local_cid,
3164 .receive_fn = receive_fn}),
3165 BuildCoc(proxy,
3166 CocParameters{.handle = handle,
3167 .local_cid = static_cast<uint16_t>(local_cid + 1),
3168 .receive_fn = receive_fn}),
3169 BuildCoc(proxy,
3170 CocParameters{.handle = handle,
3171 .local_cid = static_cast<uint16_t>(local_cid + 2),
3172 .receive_fn = receive_fn}),
3173 BuildCoc(proxy,
3174 CocParameters{.handle = handle,
3175 .local_cid = static_cast<uint16_t>(local_cid + 3),
3176 .receive_fn = receive_fn}),
3177 BuildCoc(proxy,
3178 CocParameters{.handle = handle,
3179 .local_cid = static_cast<uint16_t>(local_cid + 4),
3180 .receive_fn = receive_fn}),
3181 };
3182
3183 std::array<uint8_t, kFirstKFrameOverAclMinSize + capture.payload.size()>
3184 hci_arr;
3185 hci_arr.fill(0);
3186
3187 Result<emboss::AclDataFrameWriter> acl =
3188 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3189 acl->header().handle().Write(handle);
3190 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
3191 capture.payload.size());
3192
3193 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3194 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3195 kframe.pdu_length().Write(capture.payload.size() + kSduLengthFieldSize);
3196 kframe.sdu_length().Write(capture.payload.size());
3197
3198 for (int i = 0; i < kNumChannels; ++i) {
3199 kframe.channel_id().Write(local_cid + i);
3200
3201 std::copy(capture.payload.begin(),
3202 capture.payload.end(),
3203 hci_arr.begin() + kFirstKFrameOverAclMinSize);
3204
3205 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3206 proxy.HandleH4HciFromController(std::move(h4_packet));
3207
3208 std::for_each(capture.payload.begin(),
3209 capture.payload.end(),
3210 [](uint8_t& byte) { ++byte; });
3211 }
3212
3213 EXPECT_EQ(capture.sends_called, kNumChannels);
3214 }
3215
TEST(L2capCocReadTest,ChannelStoppageDoNotAffectOtherChannels)3216 TEST(L2capCocReadTest, ChannelStoppageDoNotAffectOtherChannels) {
3217 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3218 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3219 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3220 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3221 ProxyHost proxy = ProxyHost(
3222 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3223
3224 struct {
3225 int sends_called = 0;
3226 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
3227 } capture;
3228
3229 constexpr int kNumChannels = 5;
3230 uint16_t local_cid = 123;
3231 uint16_t handle = 456;
3232 auto receive_fn = [&capture](pw::span<uint8_t> payload) {
3233 ++capture.sends_called;
3234 EXPECT_TRUE(std::equal(payload.begin(),
3235 payload.end(),
3236 capture.payload.begin(),
3237 capture.payload.end()));
3238 };
3239 std::array<L2capCoc, kNumChannels> channels{
3240 BuildCoc(proxy,
3241 CocParameters{.handle = handle,
3242 .local_cid = local_cid,
3243 .receive_fn = receive_fn}),
3244 BuildCoc(proxy,
3245 CocParameters{.handle = handle,
3246 .local_cid = static_cast<uint16_t>(local_cid + 1),
3247 .receive_fn = receive_fn}),
3248 BuildCoc(proxy,
3249 CocParameters{.handle = handle,
3250 .local_cid = static_cast<uint16_t>(local_cid + 2),
3251 .receive_fn = receive_fn}),
3252 BuildCoc(proxy,
3253 CocParameters{.handle = handle,
3254 .local_cid = static_cast<uint16_t>(local_cid + 3),
3255 .receive_fn = receive_fn}),
3256 BuildCoc(proxy,
3257 CocParameters{.handle = handle,
3258 .local_cid = static_cast<uint16_t>(local_cid + 4),
3259 .receive_fn = receive_fn}),
3260 };
3261
3262 // Stop the 2nd and 4th of the 5 channels.
3263 EXPECT_EQ(channels[1].Stop(), PW_STATUS_OK);
3264 EXPECT_EQ(channels[3].Stop(), PW_STATUS_OK);
3265
3266 std::array<uint8_t, kFirstKFrameOverAclMinSize + capture.payload.size()>
3267 hci_arr;
3268 hci_arr.fill(0);
3269
3270 Result<emboss::AclDataFrameWriter> acl =
3271 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3272 acl->header().handle().Write(handle);
3273 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
3274 capture.payload.size());
3275
3276 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3277 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3278 kframe.pdu_length().Write(capture.payload.size() + kSduLengthFieldSize);
3279 kframe.sdu_length().Write(capture.payload.size());
3280
3281 for (int i = 0; i < kNumChannels; ++i) {
3282 // Still send packets to the stopped channels, so we can validate that it
3283 // does not cause issues.
3284 kframe.channel_id().Write(local_cid + i);
3285
3286 std::copy(capture.payload.begin(),
3287 capture.payload.end(),
3288 hci_arr.begin() + kFirstKFrameOverAclMinSize);
3289
3290 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3291 proxy.HandleH4HciFromController(std::move(h4_packet));
3292
3293 std::for_each(capture.payload.begin(),
3294 capture.payload.end(),
3295 [](uint8_t& byte) { ++byte; });
3296 }
3297
3298 EXPECT_EQ(capture.sends_called, kNumChannels - 2);
3299 }
3300
TEST(L2capCocReadTest,ReadDropsSduSentOver2Segments)3301 TEST(L2capCocReadTest, ReadDropsSduSentOver2Segments) {
3302 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3303 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3304 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3305 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3306 ProxyHost proxy = ProxyHost(
3307 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3308
3309 int sends_called = 0;
3310 uint16_t local_cid = 234;
3311 uint16_t handle = 123;
3312 L2capCoc channel = BuildCoc(
3313 proxy,
3314 CocParameters{.handle = handle,
3315 .local_cid = local_cid,
3316 .receive_fn = [&sends_called](pw::span<uint8_t> payload) {
3317 EXPECT_EQ(payload.size(), 0ul);
3318 ++sends_called;
3319 }});
3320
3321 // Send L2CAP SDU segmented over 2 frames.
3322 constexpr uint16_t k1stPayloadSize = 13;
3323 constexpr uint16_t k2ndPayloadSize = 19;
3324 constexpr uint16_t kSduLength = k1stPayloadSize + k2ndPayloadSize;
3325
3326 std::array<uint8_t,
3327 kFirstKFrameOverAclMinSize +
3328 std::max(k1stPayloadSize, k2ndPayloadSize)>
3329 hci_arr;
3330 hci_arr.fill(0);
3331 H4PacketWithHci h4_1st_segment{
3332 emboss::H4PacketType::ACL_DATA,
3333 pw::span<uint8_t>(hci_arr.data(),
3334 kFirstKFrameOverAclMinSize + k1stPayloadSize)};
3335
3336 Result<emboss::AclDataFrameWriter> acl =
3337 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3338 acl->header().handle().Write(handle);
3339 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes() +
3340 k1stPayloadSize);
3341
3342 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3343 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3344 kframe.pdu_length().Write(kSduLengthFieldSize + k1stPayloadSize);
3345 kframe.channel_id().Write(local_cid);
3346 kframe.sdu_length().Write(kSduLength);
3347
3348 proxy.HandleH4HciFromController(std::move(h4_1st_segment));
3349
3350 // Send 2nd segment.
3351 acl->data_total_length().Write(emboss::SubsequentKFrame::MinSizeInBytes() +
3352 k2ndPayloadSize);
3353 kframe.pdu_length().Write(k2ndPayloadSize);
3354 H4PacketWithHci h4_2nd_segment{
3355 emboss::H4PacketType::ACL_DATA,
3356 pw::span<uint8_t>(hci_arr.data(),
3357 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
3358 emboss::SubsequentKFrame::MinSizeInBytes() +
3359 k2ndPayloadSize)};
3360
3361 proxy.HandleH4HciFromController(std::move(h4_2nd_segment));
3362
3363 // Now ensure a non-segmented packet with size 0 payload is read.
3364 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3365 kframe.pdu_length().Write(kSduLengthFieldSize);
3366 kframe.sdu_length().Write(0);
3367 H4PacketWithHci h4_packet{
3368 emboss::H4PacketType::ACL_DATA,
3369 pw::span<uint8_t>(hci_arr.data(), kFirstKFrameOverAclMinSize)};
3370
3371 proxy.HandleH4HciFromController(std::move(h4_packet));
3372
3373 EXPECT_EQ(sends_called, 1);
3374 }
3375
TEST(L2capCocReadTest,ReadDropsSduSentOver4Segments)3376 TEST(L2capCocReadTest, ReadDropsSduSentOver4Segments) {
3377 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3378 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3379 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3380 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3381 ProxyHost proxy = ProxyHost(
3382 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3383
3384 int sends_called = 0;
3385 uint16_t handle = 123;
3386 uint16_t local_cid = 234;
3387 L2capCoc channel = BuildCoc(
3388 proxy,
3389 CocParameters{
3390 .handle = handle,
3391 .local_cid = local_cid,
3392 .receive_fn =
3393 [&sends_called]([[maybe_unused]] pw::span<uint8_t> payload) {
3394 EXPECT_EQ(payload.size(), 0ul);
3395 ++sends_called;
3396 }});
3397
3398 // Send L2CAP SDU segmented over 4 frames with equal PDU length.
3399 constexpr uint16_t kPduLength = 13;
3400 ASSERT_GE(kPduLength, kSduLengthFieldSize);
3401 constexpr uint16_t kSduLength = 4 * kPduLength - kSduLengthFieldSize;
3402
3403 std::array<uint8_t,
3404 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
3405 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + kPduLength>
3406 hci_arr;
3407 hci_arr.fill(0);
3408 H4PacketWithHci h4_1st_segment{emboss::H4PacketType::ACL_DATA, hci_arr};
3409
3410 Result<emboss::AclDataFrameWriter> acl =
3411 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3412 acl->header().handle().Write(handle);
3413 acl->data_total_length().Write(
3414 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + kPduLength);
3415
3416 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3417 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3418 kframe.pdu_length().Write(kPduLength);
3419 kframe.channel_id().Write(local_cid);
3420 kframe.sdu_length().Write(kSduLength);
3421
3422 proxy.HandleH4HciFromController(std::move(h4_1st_segment));
3423
3424 H4PacketWithHci h4_2nd_segment{emboss::H4PacketType::ACL_DATA, hci_arr};
3425 proxy.HandleH4HciFromController(std::move(h4_2nd_segment));
3426
3427 H4PacketWithHci h4_3rd_segment{emboss::H4PacketType::ACL_DATA, hci_arr};
3428 proxy.HandleH4HciFromController(std::move(h4_3rd_segment));
3429
3430 H4PacketWithHci h4_4th_segment{emboss::H4PacketType::ACL_DATA, hci_arr};
3431 proxy.HandleH4HciFromController(std::move(h4_4th_segment));
3432
3433 // Now ensure a non-segmented packet with size 0 payload is read.
3434 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3435 kframe.pdu_length().Write(kSduLengthFieldSize);
3436 kframe.sdu_length().Write(0);
3437 H4PacketWithHci h4_packet{
3438 emboss::H4PacketType::ACL_DATA,
3439 pw::span(hci_arr.data(), kFirstKFrameOverAclMinSize)};
3440
3441 proxy.HandleH4HciFromController(std::move(h4_packet));
3442
3443 EXPECT_EQ(sends_called, 1);
3444 }
3445
TEST(L2capCocReadTest,NonCocAclPacketPassesThroughToHost)3446 TEST(L2capCocReadTest, NonCocAclPacketPassesThroughToHost) {
3447 struct {
3448 int sends_called = 0;
3449 uint16_t handle = 123;
3450 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
3451 } capture;
3452
3453 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3454 [&capture](H4PacketWithHci&& packet) {
3455 ++capture.sends_called;
3456 Result<emboss::AclDataFrameWriter> acl =
3457 MakeEmbossWriter<emboss::AclDataFrameWriter>(packet.GetHciSpan());
3458 EXPECT_EQ(acl->header().handle().Read(), capture.handle);
3459 emboss::BFrameView bframe =
3460 emboss::MakeBFrameView(acl->payload().BackingStorage().data(),
3461 acl->data_total_length().Read());
3462 for (size_t i = 0; i < capture.expected_payload.size(); ++i) {
3463 EXPECT_EQ(bframe.payload()[i].Read(), capture.expected_payload[i]);
3464 }
3465 });
3466 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3467 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3468 ProxyHost proxy = ProxyHost(
3469 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3470
3471 // Acquire unused CoC to validate that doing so does not interfere.
3472 L2capCoc channel = BuildCoc(
3473 proxy,
3474 CocParameters{
3475 .handle = capture.handle,
3476 .receive_fn = []([[maybe_unused]] pw::span<uint8_t> payload) {
3477 FAIL();
3478 }});
3479
3480 std::array<uint8_t,
3481 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
3482 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
3483 capture.expected_payload.size()>
3484 hci_arr;
3485 hci_arr.fill(0);
3486 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3487
3488 Result<emboss::AclDataFrameWriter> acl =
3489 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3490 acl->header().handle().Write(capture.handle);
3491 acl->data_total_length().Write(
3492 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
3493 capture.expected_payload.size());
3494
3495 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
3496 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3497 bframe.pdu_length().Write(capture.expected_payload.size());
3498 bframe.channel_id().Write(111);
3499 std::copy(capture.expected_payload.begin(),
3500 capture.expected_payload.end(),
3501 hci_arr.begin() +
3502 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
3503 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
3504
3505 // Send ACL packet that should be forwarded to host.
3506 proxy.HandleH4HciFromController(std::move(h4_packet));
3507
3508 EXPECT_EQ(capture.sends_called, 1);
3509 }
3510
TEST(L2capCocReadTest,FragmentedPduStopsChannelWithoutDisruptingOtherChannel)3511 TEST(L2capCocReadTest, FragmentedPduStopsChannelWithoutDisruptingOtherChannel) {
3512 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3513 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3514 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3515 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3516 ProxyHost proxy = ProxyHost(
3517 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3518
3519 int events_called = 0;
3520 uint16_t handle = 123;
3521 uint16_t local_cid = 234;
3522 L2capCoc channel = BuildCoc(
3523 proxy,
3524 CocParameters{
3525 .handle = handle,
3526 .local_cid = local_cid,
3527 .receive_fn =
3528 []([[maybe_unused]] pw::span<uint8_t> payload) { FAIL(); },
3529 .event_fn =
3530 [&events_called](L2capCoc::Event event) {
3531 EXPECT_EQ(event, L2capCoc::Event::kRxFragmented);
3532 ++events_called;
3533 }});
3534
3535 constexpr uint8_t kPduLength = 19;
3536 ASSERT_GT(kPduLength, kSduLengthFieldSize);
3537 constexpr uint8_t kSduLength = kPduLength - kSduLengthFieldSize;
3538
3539 // Send first ACL fragment. Should stop channel with error.
3540 std::array<uint8_t, emboss::AclDataFrame::MinSizeInBytes() + kSduLength>
3541 hci_arr;
3542 hci_arr.fill(0);
3543 H4PacketWithHci h4_1st_fragment{
3544 emboss::H4PacketType::ACL_DATA,
3545 pw::span(hci_arr.data(), kFirstKFrameOverAclMinSize)};
3546
3547 Result<emboss::AclDataFrameWriter> acl =
3548 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3549 acl->header().handle().Write(handle);
3550 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3551
3552 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3553 acl->payload().BackingStorage().data(), acl->data_total_length().Read());
3554 kframe.pdu_length().Write(kPduLength);
3555 kframe.channel_id().Write(local_cid);
3556 kframe.sdu_length().Write(kSduLength);
3557
3558 proxy.HandleH4HciFromController(std::move(h4_1st_fragment));
3559
3560 // Ensure 2nd fragment is silently discarded.
3561 acl->header().packet_boundary_flag().Write(
3562 emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT);
3563 acl->data_total_length().Write(kSduLength / 2);
3564 H4PacketWithHci h4_2nd_fragment{
3565 emboss::H4PacketType::ACL_DATA,
3566 pw::span<uint8_t>(
3567 hci_arr.data(),
3568 emboss::AclDataFrame::MinSizeInBytes() + kSduLength / 2)};
3569 proxy.HandleH4HciFromController(std::move(h4_2nd_fragment));
3570
3571 // Ensure 3rd fragment is silently discarded.
3572 if (kSduLength % 2 == 1) {
3573 acl->data_total_length().Write(kSduLength / 2 + 1);
3574 }
3575 H4PacketWithHci h4_3rd_fragment{
3576 emboss::H4PacketType::ACL_DATA,
3577 pw::span<uint8_t>(hci_arr.data(),
3578 emboss::AclDataFrame::MinSizeInBytes() +
3579 kSduLength / 2 + (kSduLength % 2))};
3580 proxy.HandleH4HciFromController(std::move(h4_3rd_fragment));
3581
3582 EXPECT_EQ(events_called, 1);
3583
3584 int reads_called = 0;
3585 uint16_t different_local_cid = 432;
3586 L2capCoc different_channel = BuildCoc(
3587 proxy,
3588 CocParameters{
3589 .handle = handle,
3590 .local_cid = different_local_cid,
3591 .receive_fn =
3592 [&reads_called]([[maybe_unused]] pw::span<uint8_t> payload) {
3593 ++reads_called;
3594 },
3595 .event_fn = []([[maybe_unused]] L2capCoc::Event event) { FAIL(); }});
3596
3597 // Ensure different channel can still receive valid payload.
3598 std::array<uint8_t, kFirstKFrameOverAclMinSize> other_hci_arr;
3599 other_hci_arr.fill(0);
3600 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, other_hci_arr};
3601
3602 Result<emboss::AclDataFrameWriter> other_acl =
3603 MakeEmbossWriter<emboss::AclDataFrameWriter>(other_hci_arr);
3604 other_acl->header().handle().Write(handle);
3605 other_acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3606
3607 emboss::FirstKFrameWriter other_kframe =
3608 emboss::MakeFirstKFrameView(other_acl->payload().BackingStorage().data(),
3609 other_acl->data_total_length().Read());
3610 other_kframe.pdu_length().Write(kSduLengthFieldSize);
3611 other_kframe.channel_id().Write(different_local_cid);
3612
3613 proxy.HandleH4HciFromController(std::move(h4_packet));
3614
3615 EXPECT_EQ(reads_called, 1);
3616 }
3617
TEST(L2capCocReadTest,UnexpectedContinuingFragmentStopsChannel)3618 TEST(L2capCocReadTest, UnexpectedContinuingFragmentStopsChannel) {
3619 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3620 [](H4PacketWithHci&&) {});
3621 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3622 [](H4PacketWithH4&&) {});
3623 ProxyHost proxy = ProxyHost(
3624 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3625
3626 int events_received = 0;
3627 uint16_t handle = 123;
3628 uint16_t local_cid = 234;
3629 L2capCoc channel = BuildCoc(
3630 proxy,
3631 CocParameters{.handle = handle,
3632 .local_cid = local_cid,
3633 .receive_fn = [](span<uint8_t>) { FAIL(); },
3634 .event_fn =
3635 [&events_received](L2capCoc::Event event) {
3636 ++events_received;
3637 EXPECT_EQ(event, L2capCoc::Event::kRxFragmented);
3638 }});
3639
3640 std::array<uint8_t, kFirstKFrameOverAclMinSize> hci_arr;
3641 hci_arr.fill(0);
3642 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3643
3644 Result<emboss::AclDataFrameWriter> acl =
3645 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3646 acl->header().handle().Write(handle);
3647 acl->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3648 acl->header().packet_boundary_flag().Write(
3649 emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT);
3650
3651 emboss::FirstKFrameWriter kframe = emboss::MakeFirstKFrameView(
3652 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
3653 kframe.pdu_length().Write(kSduLengthFieldSize);
3654 kframe.channel_id().Write(local_cid);
3655
3656 proxy.HandleH4HciFromController(std::move(h4_packet));
3657
3658 EXPECT_EQ(events_received, 1);
3659 }
3660
TEST(L2capCocReadTest,AclFrameWithIncompleteL2capHeaderForwardedToHost)3661 TEST(L2capCocReadTest, AclFrameWithIncompleteL2capHeaderForwardedToHost) {
3662 int sends_to_host_called = 0;
3663 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3664 [&sends_to_host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
3665 ++sends_to_host_called;
3666 });
3667 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3668 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3669 ProxyHost proxy = ProxyHost(
3670 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3671
3672 uint16_t handle = 123;
3673 L2capCoc channel = BuildCoc(proxy, CocParameters{.handle = handle});
3674
3675 std::array<uint8_t, emboss::AclDataFrameHeader::IntrinsicSizeInBytes()>
3676 hci_arr;
3677 hci_arr.fill(0);
3678 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
3679
3680 Result<emboss::AclDataFrameWriter> acl =
3681 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3682 acl->header().handle().Write(handle);
3683 acl->data_total_length().Write(0);
3684
3685 proxy.HandleH4HciFromController(std::move(h4_packet));
3686
3687 EXPECT_EQ(sends_to_host_called, 1);
3688 }
3689
TEST(L2capCocReadTest,FragmentedPduDoesNotInterfereWithOtherChannels)3690 TEST(L2capCocReadTest, FragmentedPduDoesNotInterfereWithOtherChannels) {
3691 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3692 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3693 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3694 []([[maybe_unused]] H4PacketWithH4&& packet) {});
3695 ProxyHost proxy = ProxyHost(
3696 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
3697
3698 uint16_t handle_frag = 0x123, handle_fine = 0x234;
3699 uint16_t cid_frag = 0x345, cid_fine = 0x456;
3700 int packets_received = 0;
3701 auto receive_fn =
3702 [&packets_received]([[maybe_unused]] pw::span<uint8_t> payload) {
3703 ++packets_received;
3704 };
3705 L2capCoc frag_channel = BuildCoc(proxy,
3706 CocParameters{
3707 .handle = handle_frag,
3708 .local_cid = cid_frag,
3709 .receive_fn = receive_fn,
3710 });
3711 L2capCoc fine_channel = BuildCoc(proxy,
3712 CocParameters{
3713 .handle = handle_fine,
3714 .local_cid = cid_fine,
3715 .receive_fn = receive_fn,
3716 });
3717
3718 // Order of receptions:
3719 // 1. 1st of 3 fragments to frag_channel.
3720 // 2. Non-fragmented PDU to fine_channel.
3721 // 3. 2nd of 3 fragments to frag_channel.
3722 // 4. Non-fragmented PDU to fine_channel.
3723 // 5. 3rd of 3 fragments to frag_channel.
3724 // 6. Non-fragmented PDU to fine_channel.
3725
3726 constexpr uint8_t kPduLength = 14;
3727 ASSERT_GT(kPduLength, kSduLengthFieldSize);
3728 constexpr uint8_t kSduLength = kPduLength - kSduLengthFieldSize;
3729
3730 // 1. 1st of 3 fragments to frag_channel.
3731 std::array<uint8_t, emboss::AclDataFrame::MinSizeInBytes() + kSduLength>
3732 frag_hci_arr;
3733 frag_hci_arr.fill(0);
3734 H4PacketWithHci h4_1st_fragment{
3735 emboss::H4PacketType::ACL_DATA,
3736 pw::span(frag_hci_arr.data(), kFirstKFrameOverAclMinSize)};
3737
3738 Result<emboss::AclDataFrameWriter> acl_frag =
3739 MakeEmbossWriter<emboss::AclDataFrameWriter>(frag_hci_arr);
3740 acl_frag->header().handle().Write(handle_frag);
3741 acl_frag->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3742
3743 emboss::FirstKFrameWriter kframe_frag =
3744 emboss::MakeFirstKFrameView(acl_frag->payload().BackingStorage().data(),
3745 acl_frag->data_total_length().Read());
3746 kframe_frag.pdu_length().Write(kPduLength);
3747 kframe_frag.channel_id().Write(cid_frag);
3748 kframe_frag.sdu_length().Write(kSduLength);
3749
3750 proxy.HandleH4HciFromController(std::move(h4_1st_fragment));
3751
3752 // 2. Non-fragmented PDU to fine_channel.
3753 std::array<uint8_t, kFirstKFrameOverAclMinSize> fine_hci_arr;
3754 fine_hci_arr.fill(0);
3755 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA,
3756 pw::span(fine_hci_arr)};
3757
3758 Result<emboss::AclDataFrameWriter> acl_fine =
3759 MakeEmbossWriter<emboss::AclDataFrameWriter>(fine_hci_arr);
3760 acl_fine->header().handle().Write(handle_fine);
3761 acl_fine->data_total_length().Write(emboss::FirstKFrame::MinSizeInBytes());
3762
3763 emboss::FirstKFrameWriter kframe_fine =
3764 emboss::MakeFirstKFrameView(acl_fine->payload().BackingStorage().data(),
3765 acl_fine->data_total_length().Read());
3766 kframe_fine.pdu_length().Write(kSduLengthFieldSize);
3767 kframe_fine.channel_id().Write(cid_fine);
3768 kframe_fine.sdu_length().Write(0);
3769
3770 proxy.HandleH4HciFromController(std::move(h4_packet));
3771
3772 // 3. 2nd of 3 fragments to frag_channel.
3773 acl_frag->header().packet_boundary_flag().Write(
3774 emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT);
3775 acl_frag->data_total_length().Write(kSduLength / 2);
3776 H4PacketWithHci h4_2nd_fragment{
3777 emboss::H4PacketType::ACL_DATA,
3778 pw::span<uint8_t>(
3779 frag_hci_arr.data(),
3780 emboss::AclDataFrame::MinSizeInBytes() + kSduLength / 2)};
3781 proxy.HandleH4HciFromController(std::move(h4_2nd_fragment));
3782
3783 // 4. Non-fragmented PDU to fine_channel.
3784 H4PacketWithHci h4_packet_2{emboss::H4PacketType::ACL_DATA,
3785 pw::span(fine_hci_arr)};
3786 proxy.HandleH4HciFromController(std::move(h4_packet_2));
3787
3788 // 5. 3rd of 3 fragments to frag_channel.
3789 if (kSduLength % 2 == 1) {
3790 acl_frag->data_total_length().Write(kSduLength / 2 + 1);
3791 }
3792 H4PacketWithHci h4_3rd_fragment{
3793 emboss::H4PacketType::ACL_DATA,
3794 pw::span<uint8_t>(frag_hci_arr.data(),
3795 emboss::AclDataFrame::MinSizeInBytes() +
3796 kSduLength / 2 + (kSduLength % 2))};
3797 proxy.HandleH4HciFromController(std::move(h4_3rd_fragment));
3798
3799 // 6. Non-fragmented PDU to fine_channel.
3800 H4PacketWithHci h4_packet_3{emboss::H4PacketType::ACL_DATA,
3801 pw::span(fine_hci_arr)};
3802 proxy.HandleH4HciFromController(std::move(h4_packet_3));
3803
3804 EXPECT_EQ(packets_received, 3);
3805 }
3806 // ########## L2capCocQueueTest
3807
TEST(L2capCocQueueTest,ReadBufferResponseDrainsQueue)3808 TEST(L2capCocQueueTest, ReadBufferResponseDrainsQueue) {
3809 size_t sends_called = 0;
3810
3811 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3812 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3813 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3814 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
3815 ++sends_called;
3816 });
3817 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3818 std::move(send_to_controller_fn),
3819 L2capCoc::QueueCapacity());
3820
3821 L2capCoc channel =
3822 BuildCoc(proxy, CocParameters{.tx_credits = L2capCoc::QueueCapacity()});
3823
3824 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
3825 for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
3826 EXPECT_EQ(channel.Write({}), PW_STATUS_OK);
3827 }
3828 EXPECT_EQ(channel.Write({}), PW_STATUS_UNAVAILABLE);
3829 EXPECT_EQ(sends_called, 0u);
3830
3831 PW_TEST_EXPECT_OK(
3832 SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
3833
3834 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
3835 }
3836
TEST(L2capCocQueueTest,NocpEventDrainsQueue)3837 TEST(L2capCocQueueTest, NocpEventDrainsQueue) {
3838 size_t sends_called = 0;
3839
3840 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3841 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3842 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3843 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
3844 ++sends_called;
3845 });
3846 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3847 std::move(send_to_controller_fn),
3848 L2capCoc::QueueCapacity());
3849 PW_TEST_EXPECT_OK(
3850 SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
3851
3852 uint16_t handle = 123;
3853 L2capCoc channel =
3854 BuildCoc(proxy,
3855 CocParameters{.handle = handle,
3856 .tx_credits = 2 * L2capCoc::QueueCapacity()});
3857
3858 for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
3859 EXPECT_EQ(channel.Write({}), PW_STATUS_OK);
3860 }
3861
3862 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
3863 for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
3864 EXPECT_EQ(channel.Write({}), PW_STATUS_OK);
3865 }
3866 EXPECT_EQ(channel.Write({}), PW_STATUS_UNAVAILABLE);
3867 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
3868
3869 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
3870 proxy,
3871 FlatMap<uint16_t, uint16_t, 1>({{{handle, L2capCoc::QueueCapacity()}}})));
3872
3873 EXPECT_EQ(sends_called, 2 * L2capCoc::QueueCapacity());
3874 }
3875
TEST(L2capCocQueueTest,RemovingLrdWriteChannelDoesNotInvalidateRoundRobin)3876 TEST(L2capCocQueueTest, RemovingLrdWriteChannelDoesNotInvalidateRoundRobin) {
3877 size_t sends_called = 0;
3878 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3879 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3880 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3881 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
3882 ++sends_called;
3883 });
3884 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3885 std::move(send_to_controller_fn),
3886 L2capCoc::QueueCapacity());
3887 PW_TEST_EXPECT_OK(
3888 SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
3889 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), L2capCoc::QueueCapacity());
3890
3891 uint16_t handle = 123;
3892 std::array<uint16_t, 3> remote_cids = {1, 2, 3};
3893 L2capCoc chan_left = BuildCoc(
3894 proxy,
3895 CocParameters{
3896 .handle = handle, .remote_cid = remote_cids[0], .tx_credits = 1});
3897 std::optional<L2capCoc> chan_middle =
3898 BuildCoc(proxy,
3899 CocParameters{.handle = handle,
3900 .remote_cid = remote_cids[1],
3901 .tx_credits = L2capCoc::QueueCapacity() + 1});
3902 L2capCoc chan_right = BuildCoc(
3903 proxy,
3904 CocParameters{
3905 .handle = handle, .remote_cid = remote_cids[2], .tx_credits = 1});
3906
3907 // We have 3 channels. Make it so LRD write channel iterator is on the
3908 // middle channel, then release that channel and ensure the other two are
3909 // still reached in the round robin.
3910
3911 // Queue a packet in middle channel.
3912 for (size_t i = 0; i < L2capCoc::QueueCapacity() + 1; ++i) {
3913 EXPECT_EQ(chan_middle->Write({}), PW_STATUS_OK);
3914 }
3915 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
3916
3917 // Make middle channel the LRD write channel.
3918 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
3919 proxy, FlatMap<uint16_t, uint16_t, 1>({{{handle, 1}}})));
3920 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity() + 1);
3921
3922 // Queue a packet each in left and right channels.
3923 EXPECT_EQ(chan_left.Write({}), PW_STATUS_OK);
3924 EXPECT_EQ(chan_right.Write({}), PW_STATUS_OK);
3925 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity() + 1);
3926
3927 // Drop middle channel. LRD write channel iterator should still be valid.
3928 chan_middle.reset();
3929
3930 // Confirm packets in remaining two channels are sent in round robin.
3931 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
3932 proxy, FlatMap<uint16_t, uint16_t, 1>({{{handle, 2}}})));
3933 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity() + 3);
3934 }
3935
3936 // ########## L2capSignalingTest
3937
TEST(L2capSignalingTest,FlowControlCreditIndDrainsQueue)3938 TEST(L2capSignalingTest, FlowControlCreditIndDrainsQueue) {
3939 size_t sends_called = 0;
3940
3941 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
3942 []([[maybe_unused]] H4PacketWithHci&& packet) {});
3943 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
3944 [&sends_called]([[maybe_unused]] H4PacketWithH4&& packet) {
3945 ++sends_called;
3946 });
3947 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
3948 std::move(send_to_controller_fn),
3949 L2capCoc::QueueCapacity());
3950 PW_TEST_EXPECT_OK(
3951 SendLeReadBufferResponseFromController(proxy, L2capCoc::QueueCapacity()));
3952 EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), L2capCoc::QueueCapacity());
3953
3954 uint16_t handle = 123;
3955 uint16_t remote_cid = 456;
3956 L2capCoc channel = BuildCoc(
3957 proxy,
3958 CocParameters{
3959 .handle = handle, .remote_cid = remote_cid, .tx_credits = 0});
3960
3961 for (size_t i = 0; i < L2capCoc::QueueCapacity(); ++i) {
3962 EXPECT_EQ(channel.Write({}), PW_STATUS_OK);
3963 }
3964 EXPECT_EQ(channel.Write({}), PW_STATUS_UNAVAILABLE);
3965 EXPECT_EQ(sends_called, 0u);
3966
3967 constexpr size_t kL2capLength =
3968 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
3969 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes();
3970 constexpr size_t kHciLength =
3971 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
3972 std::array<uint8_t, kHciLength> hci_arr;
3973 hci_arr.fill(0);
3974 H4PacketWithHci flow_control_credit_ind{emboss::H4PacketType::ACL_DATA,
3975 pw::span(hci_arr.data(), kHciLength)};
3976
3977 Result<emboss::AclDataFrameWriter> acl =
3978 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
3979 acl->header().handle().Write(handle);
3980 acl->data_total_length().Write(kL2capLength);
3981
3982 emboss::CFrameWriter l2cap = emboss::MakeCFrameView(
3983 acl->payload().BackingStorage().data(), kL2capLength);
3984 l2cap.pdu_length().Write(
3985 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
3986 // 0x0005 = LE-U fixed signaling channel ID.
3987 l2cap.channel_id().Write(0x0005);
3988
3989 emboss::L2capFlowControlCreditIndWriter ind =
3990 emboss::MakeL2capFlowControlCreditIndView(
3991 l2cap.payload().BackingStorage().data(),
3992 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
3993 ind.command_header().code().Write(
3994 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
3995 ind.command_header().data_length().Write(
3996 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
3997 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
3998 ind.cid().Write(remote_cid);
3999 ind.credits().Write(L2capCoc::QueueCapacity());
4000
4001 proxy.HandleH4HciFromController(std::move(flow_control_credit_ind));
4002
4003 EXPECT_EQ(sends_called, L2capCoc::QueueCapacity());
4004 }
4005
TEST(L2capSignalingTest,ChannelClosedWithErrorIfCreditsExceeded)4006 TEST(L2capSignalingTest, ChannelClosedWithErrorIfCreditsExceeded) {
4007 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4008 []([[maybe_unused]] H4PacketWithHci&& packet) {});
4009 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4010 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4011
4012 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4013 std::move(send_to_controller_fn),
4014 L2capCoc::QueueCapacity());
4015
4016 uint16_t handle = 123;
4017 uint16_t remote_cid = 456;
4018 int events_received = 0;
4019 L2capCoc channel = BuildCoc(
4020 proxy,
4021 CocParameters{
4022 .handle = handle,
4023 .remote_cid = remote_cid,
4024 // Initialize with max credit count.
4025 .tx_credits =
4026 emboss::L2capLeCreditBasedConnectionReq::max_credit_value(),
4027 .event_fn = [&events_received](L2capCoc::Event event) {
4028 EXPECT_EQ(event, L2capCoc::Event::kRxInvalid);
4029 ++events_received;
4030 }});
4031
4032 constexpr size_t kL2capLength =
4033 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
4034 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes();
4035 constexpr size_t kHciLength =
4036 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
4037 std::array<uint8_t, kHciLength> hci_arr;
4038 hci_arr.fill(0);
4039 H4PacketWithHci flow_control_credit_ind{emboss::H4PacketType::ACL_DATA,
4040 pw::span(hci_arr.data(), kHciLength)};
4041
4042 Result<emboss::AclDataFrameWriter> acl =
4043 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
4044 acl->header().handle().Write(handle);
4045 acl->data_total_length().Write(kL2capLength);
4046
4047 emboss::CFrameWriter l2cap =
4048 emboss::MakeCFrameView(acl->payload().BackingStorage().data(),
4049 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
4050 l2cap.pdu_length().Write(
4051 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
4052 // 0x0005 = LE-U fixed signaling channel ID.
4053 l2cap.channel_id().Write(0x0005);
4054
4055 emboss::L2capFlowControlCreditIndWriter ind =
4056 emboss::MakeL2capFlowControlCreditIndView(
4057 l2cap.payload().BackingStorage().data(),
4058 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
4059 ind.command_header().code().Write(
4060 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
4061 ind.command_header().data_length().Write(
4062 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
4063 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
4064 ind.cid().Write(remote_cid);
4065 // Exceed max credit count by 1.
4066 ind.credits().Write(1);
4067
4068 proxy.HandleH4HciFromController(std::move(flow_control_credit_ind));
4069
4070 EXPECT_EQ(events_received, 1);
4071 }
4072
TEST(L2capSignalingTest,CreditIndAddressedToNonManagedChannelForwardedToHost)4073 TEST(L2capSignalingTest, CreditIndAddressedToNonManagedChannelForwardedToHost) {
4074 int forwards_to_host = 0;
4075 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4076 [&forwards_to_host](H4PacketWithHci&&) { ++forwards_to_host; });
4077 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4078 [](H4PacketWithH4&&) {});
4079
4080 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4081 std::move(send_to_controller_fn),
4082 L2capCoc::QueueCapacity());
4083
4084 uint16_t handle = 123;
4085 uint16_t remote_cid = 456;
4086 L2capCoc channel = BuildCoc(
4087 proxy, CocParameters{.handle = handle, .remote_cid = remote_cid});
4088
4089 constexpr size_t kL2capLength =
4090 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
4091 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes();
4092 constexpr size_t kHciLength =
4093 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
4094 std::array<uint8_t, kHciLength> hci_arr;
4095 hci_arr.fill(0);
4096 H4PacketWithHci flow_control_credit_ind{emboss::H4PacketType::ACL_DATA,
4097 pw::span(hci_arr.data(), kHciLength)};
4098
4099 Result<emboss::AclDataFrameWriter> acl =
4100 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr);
4101 acl->header().handle().Write(handle);
4102 acl->data_total_length().Write(kL2capLength);
4103
4104 emboss::CFrameWriter l2cap =
4105 emboss::MakeCFrameView(acl->payload().BackingStorage().data(),
4106 emboss::BasicL2capHeader::IntrinsicSizeInBytes());
4107 l2cap.pdu_length().Write(
4108 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
4109 // 0x0005 = LE-U fixed signaling channel ID.
4110 l2cap.channel_id().Write(0x0005);
4111
4112 emboss::L2capFlowControlCreditIndWriter ind =
4113 emboss::MakeL2capFlowControlCreditIndView(
4114 l2cap.payload().BackingStorage().data(),
4115 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
4116 ind.command_header().code().Write(
4117 emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
4118 ind.command_header().data_length().Write(
4119 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
4120 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
4121 // Address packet to different CID on same connection.
4122 ind.cid().Write(remote_cid + 1);
4123
4124 proxy.HandleH4HciFromController(std::move(flow_control_credit_ind));
4125
4126 EXPECT_EQ(forwards_to_host, 1);
4127 }
4128
TEST(L2capSignalingTest,RxAdditionalCreditsSentOnL2capCocAcquisition)4129 TEST(L2capSignalingTest, RxAdditionalCreditsSentOnL2capCocAcquisition) {
4130 struct {
4131 uint16_t handle = 123;
4132 uint16_t local_cid = 456;
4133 uint16_t credits = 3;
4134 int sends_called = 0;
4135 } capture;
4136
4137 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4138 [](H4PacketWithHci&&) {});
4139 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4140 [&capture](H4PacketWithH4&& packet) {
4141 ++capture.sends_called;
4142 PW_TEST_ASSERT_OK_AND_ASSIGN(
4143 auto acl,
4144 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
4145 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
4146 EXPECT_EQ(
4147 acl.data_total_length().Read(),
4148 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
4149 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
4150 emboss::CFrameView cframe = emboss::MakeCFrameView(
4151 acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
4152 EXPECT_EQ(cframe.pdu_length().Read(),
4153 emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes());
4154 // 0x0005 = LE-U fixed signaling channel ID.
4155 EXPECT_EQ(cframe.channel_id().Read(), 0x0005);
4156 emboss::L2capFlowControlCreditIndView ind =
4157 emboss::MakeL2capFlowControlCreditIndView(
4158 cframe.payload().BackingStorage().data(),
4159 cframe.payload().SizeInBytes());
4160 EXPECT_EQ(ind.cid().Read(), capture.local_cid);
4161 EXPECT_EQ(ind.credits().Read(), capture.credits);
4162 });
4163
4164 ProxyHost proxy = ProxyHost(
4165 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
4166 // Allow proxy to reserve 1 LE credit.
4167 PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1));
4168
4169 L2capCoc channel =
4170 BuildCoc(proxy,
4171 CocParameters{.handle = capture.handle,
4172 .local_cid = capture.local_cid,
4173 .rx_additional_credits = capture.credits});
4174
4175 EXPECT_EQ(capture.sends_called, 1);
4176 }
4177
4178 // ########## AcluSignalingChannelTest
4179
TEST(AcluSignalingChannelTest,HandlesMultipleCommands)4180 TEST(AcluSignalingChannelTest, HandlesMultipleCommands) {
4181 std::optional<H4PacketWithHci> host_packet;
4182 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4183 [&host_packet](H4PacketWithHci&& packet) {
4184 host_packet = std::move(packet);
4185 });
4186 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
4187 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4188
4189 ProxyHost proxy = ProxyHost(
4190 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
4191
4192 constexpr uint16_t kHandle = 123;
4193
4194 // Test that the proxy can parse a CFrame containing multiple commands and
4195 // pass it through. We pack 3 CONNECTION_REQ commands into one CFrame.
4196 constexpr size_t kNumCommands = 3;
4197 constexpr size_t kCmdLen = emboss::L2capConnectionReq::IntrinsicSizeInBytes();
4198 constexpr size_t kL2capLength =
4199 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + kCmdLen * kNumCommands;
4200 constexpr size_t kHciLength =
4201 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
4202 std::array<uint8_t, kHciLength> hci_arr{};
4203 H4PacketWithHci l2cap_cframe_packet{emboss::H4PacketType::ACL_DATA,
4204 pw::span(hci_arr.data(), kHciLength)};
4205
4206 // ACL header
4207 PW_TEST_ASSERT_OK_AND_ASSIGN(
4208 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
4209 acl.header().handle().Write(kHandle);
4210 acl.data_total_length().Write(kL2capLength);
4211 EXPECT_EQ(kL2capLength, acl.payload().BackingStorage().SizeInBytes());
4212
4213 // L2CAP header
4214 auto l2cap =
4215 emboss::MakeCFrameView(acl.payload().BackingStorage().data(),
4216 acl.payload().BackingStorage().SizeInBytes());
4217 l2cap.pdu_length().Write(kNumCommands * kCmdLen);
4218 l2cap.channel_id().Write(
4219 cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING));
4220 EXPECT_TRUE(l2cap.Ok());
4221
4222 auto command_buffer =
4223 pw::span(l2cap.payload().BackingStorage().data(),
4224 l2cap.payload().BackingStorage().SizeInBytes());
4225 EXPECT_EQ(l2cap.payload().BackingStorage().SizeInBytes(),
4226 kCmdLen * kNumCommands);
4227
4228 do {
4229 // CONNECTION_REQ
4230 auto cmd_writer = emboss::MakeL2capConnectionReqView(command_buffer.data(),
4231 command_buffer.size());
4232 cmd_writer.command_header().code().Write(
4233 emboss::L2capSignalingPacketCode::CONNECTION_REQ);
4234 // Note data_length doesn't include command header.
4235 cmd_writer.command_header().data_length().Write(
4236 kCmdLen - emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
4237 cmd_writer.psm().Write(1);
4238 cmd_writer.source_cid().Write(1);
4239 EXPECT_TRUE(cmd_writer.Ok());
4240 EXPECT_EQ(cmd_writer.SizeInBytes(), kCmdLen);
4241 command_buffer = command_buffer.subspan(cmd_writer.SizeInBytes());
4242 } while (!command_buffer.empty());
4243
4244 proxy.HandleH4HciFromController(std::move(l2cap_cframe_packet));
4245 // We should get back what we sent, since the proxy doesn't consume
4246 // CONNECTION_REQ commands. It would be nice to also verify the individual
4247 // commands were parsed out but hooks don't exist for that at the time of
4248 // writing.
4249 EXPECT_TRUE(host_packet.has_value());
4250 EXPECT_EQ(host_packet->GetHciSpan().size(), kHciLength);
4251 }
4252
TEST(AcluSignalingChannelTest,InvalidPacketForwarded)4253 TEST(AcluSignalingChannelTest, InvalidPacketForwarded) {
4254 std::optional<H4PacketWithHci> host_packet;
4255 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4256 [&host_packet](H4PacketWithHci&& packet) {
4257 host_packet = std::move(packet);
4258 });
4259 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
4260 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4261
4262 ProxyHost proxy = ProxyHost(
4263 std::move(send_to_host_fn), std::move(send_to_controller_fn), 1);
4264
4265 constexpr uint16_t kHandle = 123;
4266
4267 // Test that the proxy forwards on invalid L2cap B-frames destined for
4268 // signaling channel.
4269
4270 constexpr size_t kL2capLength =
4271 emboss::BasicL2capHeader::IntrinsicSizeInBytes();
4272 constexpr size_t kHciLength =
4273 emboss::AclDataFrame::MinSizeInBytes() + kL2capLength;
4274 std::array<uint8_t, kHciLength> hci_arr{};
4275 H4PacketWithHci l2cap_cframe_packet{emboss::H4PacketType::ACL_DATA,
4276 pw::span(hci_arr.data(), kHciLength)};
4277
4278 // ACL header
4279 PW_TEST_ASSERT_OK_AND_ASSIGN(
4280 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
4281 acl.header().handle().Write(kHandle);
4282 acl.data_total_length().Write(kL2capLength);
4283 EXPECT_EQ(kL2capLength, acl.payload().BackingStorage().SizeInBytes());
4284
4285 // L2CAP header
4286 auto l2cap =
4287 emboss::MakeCFrameView(acl.payload().BackingStorage().data(),
4288 acl.payload().BackingStorage().SizeInBytes());
4289 // Invalid length, since we aren't encoding a payload.
4290 l2cap.pdu_length().Write(1);
4291 l2cap.channel_id().Write(
4292 cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING));
4293 EXPECT_FALSE(l2cap.Ok());
4294
4295 proxy.HandleH4HciFromController(std::move(l2cap_cframe_packet));
4296 // We should get back what we sent.
4297 EXPECT_TRUE(host_packet.has_value());
4298 EXPECT_EQ(host_packet->GetHciSpan().size(), kHciLength);
4299 }
4300
4301 // ########## RfcommWriteTest
4302
4303 constexpr uint8_t kBFrameOverAclMinSize =
4304 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
4305 emboss::BFrame::MinSizeInBytes();
4306
TEST(RfcommWriteTest,BasicWrite)4307 TEST(RfcommWriteTest, BasicWrite) {
4308 struct {
4309 int sends_called = 0;
4310 // First four bits 0x0 encode PB & BC flags
4311 uint16_t handle = 0x0ACB;
4312 // Length of L2CAP PDU
4313 uint16_t acl_data_total_length = 0x000C;
4314 // L2CAP header PDU length field
4315 uint16_t pdu_length = 0x0008;
4316 // Random CID
4317 uint16_t channel_id = 0x1234;
4318 // RFCOMM header
4319 std::array<uint8_t, 3> rfcomm_header = {0x19, 0xFF, 0x07};
4320 uint8_t rfcomm_credits = 0;
4321 // RFCOMM information payload
4322 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
4323 uint8_t rfcomm_fcs = 0x49;
4324
4325 // Built from the preceding values in little endian order (except payload in
4326 // big endian).
4327 std::array<uint8_t, 16> expected_hci_packet = {0xCB,
4328 0x0A,
4329 0x0C,
4330 0x00,
4331 0x08,
4332 0x00,
4333 0x34,
4334 0x12,
4335 // RFCOMM header
4336 0x19,
4337 0xFF,
4338 0x07,
4339 0x00,
4340 0xAB,
4341 0xCD,
4342 0xEF,
4343 // FCS
4344 0x49};
4345 } capture;
4346
4347 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4348 []([[maybe_unused]] H4PacketWithHci&& packet) {});
4349 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4350 [&capture](H4PacketWithH4&& packet) {
4351 ++capture.sends_called;
4352 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
4353 EXPECT_EQ(packet.GetHciSpan().size(),
4354 capture.expected_hci_packet.size());
4355 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
4356 packet.GetHciSpan().end(),
4357 capture.expected_hci_packet.begin(),
4358 capture.expected_hci_packet.end()));
4359 PW_TEST_ASSERT_OK_AND_ASSIGN(
4360 auto acl,
4361 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
4362 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
4363 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
4364 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
4365 EXPECT_EQ(acl.header().broadcast_flag().Read(),
4366 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
4367 EXPECT_EQ(acl.data_total_length().Read(),
4368 capture.acl_data_total_length);
4369 emboss::BFrameView bframe = emboss::BFrameView(
4370 acl.payload().BackingStorage().data(), acl.SizeInBytes());
4371 EXPECT_EQ(bframe.pdu_length().Read(), capture.pdu_length);
4372 EXPECT_EQ(bframe.channel_id().Read(), capture.channel_id);
4373 EXPECT_TRUE(std::equal(bframe.payload().BackingStorage().begin(),
4374 bframe.payload().BackingStorage().begin() +
4375 capture.rfcomm_header.size(),
4376 capture.rfcomm_header.begin(),
4377 capture.rfcomm_header.end()));
4378 auto rfcomm = emboss::MakeRfcommFrameView(
4379 bframe.payload().BackingStorage().data(),
4380 bframe.payload().SizeInBytes());
4381 EXPECT_TRUE(rfcomm.Ok());
4382 EXPECT_EQ(rfcomm.credits().Read(), capture.rfcomm_credits);
4383
4384 for (size_t i = 0; i < 3; ++i) {
4385 EXPECT_EQ(rfcomm.information()[i].Read(), capture.payload[i]);
4386 }
4387
4388 EXPECT_EQ(rfcomm.fcs().Read(), capture.rfcomm_fcs);
4389 });
4390
4391 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4392 std::move(send_to_controller_fn),
4393 /*le_acl_credits_to_reserve=*/0,
4394 /*br_edr_acl_credits_to_reserve=*/1);
4395 // Allow proxy to reserve 1 credit.
4396 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
4397
4398 constexpr uint8_t kMaxCredits = 10;
4399 constexpr uint16_t kMaxFrameSize = 900;
4400 constexpr uint8_t kRfcommChannel = 0x03;
4401
4402 PW_TEST_ASSERT_OK_AND_ASSIGN(
4403 auto channel,
4404 proxy.AcquireRfcommChannel(
4405 capture.handle,
4406 /*rx_config=*/
4407 RfcommChannel::Config{.cid = capture.channel_id,
4408 .max_information_length = kMaxFrameSize,
4409 .credits = kMaxCredits},
4410 /*tx_config*/
4411 RfcommChannel::Config{.cid = capture.channel_id,
4412 .max_information_length = kMaxFrameSize,
4413 .credits = kMaxCredits},
4414 kRfcommChannel,
4415 nullptr));
4416 EXPECT_EQ(channel.Write(capture.payload), PW_STATUS_OK);
4417 EXPECT_EQ(capture.sends_called, 1);
4418 }
4419
TEST(RfcommWriteTest,ExtendedWrite)4420 TEST(RfcommWriteTest, ExtendedWrite) {
4421 constexpr size_t kPayloadSize = 0x80;
4422 struct {
4423 int sends_called = 0;
4424 // First four bits 0x0 encode PB & BC flags
4425 uint16_t handle = 0x0ACB;
4426 // Length of L2CAP PDU
4427 uint16_t acl_data_total_length = 0x008A;
4428 // L2CAP header PDU length field
4429 uint16_t pdu_length = 0x0086;
4430 // Random CID
4431 uint16_t channel_id = 0x1234;
4432 // RFCOMM header
4433 std::array<uint8_t, 4> rfcomm_header = {0x19, 0xFF, 0x00, 0x01};
4434 uint8_t rfcomm_credits = 0;
4435 // RFCOMM information payload
4436 std::array<uint8_t, kPayloadSize> payload = {
4437 0xAB,
4438 0xCD,
4439 0xEF,
4440 };
4441 uint8_t rfcomm_fcs = 0x49;
4442
4443 // Built from the preceding values in little endian order (except payload in
4444 // big endian).
4445 std::array<uint8_t, kPayloadSize + 14> expected_hci_packet = {
4446 0xCB,
4447 0x0A,
4448 0x8A,
4449 0x00,
4450 0x86,
4451 0x00,
4452 0x34,
4453 0x12,
4454 // RFCOMM header
4455 0x19,
4456 0xFF,
4457 0x00,
4458 0x01,
4459 0x00,
4460 0xAB,
4461 0xCD,
4462 0xEF,
4463 };
4464 } capture;
4465
4466 // FCS
4467 capture.expected_hci_packet[capture.expected_hci_packet.size() - 1] =
4468 capture.rfcomm_fcs;
4469
4470 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4471 []([[maybe_unused]] H4PacketWithHci&& packet) {});
4472 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4473 [&capture](H4PacketWithH4&& packet) {
4474 ++capture.sends_called;
4475 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
4476 EXPECT_EQ(packet.GetHciSpan().size(),
4477 capture.expected_hci_packet.size());
4478 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
4479 packet.GetHciSpan().end(),
4480 capture.expected_hci_packet.begin(),
4481 capture.expected_hci_packet.end()));
4482 PW_TEST_ASSERT_OK_AND_ASSIGN(
4483 auto acl,
4484 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
4485 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
4486 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
4487 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
4488 EXPECT_EQ(acl.header().broadcast_flag().Read(),
4489 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
4490 EXPECT_EQ(acl.data_total_length().Read(),
4491 capture.acl_data_total_length);
4492 emboss::BFrameView bframe = emboss::BFrameView(
4493 acl.payload().BackingStorage().data(), acl.SizeInBytes());
4494 EXPECT_EQ(bframe.pdu_length().Read(), capture.pdu_length);
4495 EXPECT_EQ(bframe.channel_id().Read(), capture.channel_id);
4496 EXPECT_TRUE(std::equal(bframe.payload().BackingStorage().begin(),
4497 bframe.payload().BackingStorage().begin() +
4498 capture.rfcomm_header.size(),
4499 capture.rfcomm_header.begin(),
4500 capture.rfcomm_header.end()));
4501 auto rfcomm = emboss::MakeRfcommFrameView(
4502 bframe.payload().BackingStorage().data(),
4503 bframe.payload().SizeInBytes());
4504 EXPECT_TRUE(rfcomm.Ok());
4505 EXPECT_EQ(rfcomm.credits().Read(), capture.rfcomm_credits);
4506
4507 for (size_t i = 0; i < 3; ++i) {
4508 EXPECT_EQ(rfcomm.information()[i].Read(), capture.payload[i]);
4509 }
4510
4511 EXPECT_EQ(rfcomm.fcs().Read(), capture.rfcomm_fcs);
4512 });
4513
4514 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4515 std::move(send_to_controller_fn),
4516 /*le_acl_credits_to_reserve=*/0,
4517 /*br_edr_acl_credits_to_reserve=*/1);
4518 // Allow proxy to reserve 1 credit.
4519 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
4520
4521 constexpr uint8_t kMaxCredits = 10;
4522 constexpr uint16_t kMaxFrameSize = 900;
4523 constexpr uint8_t kRfcommChannel = 0x03;
4524
4525 PW_TEST_ASSERT_OK_AND_ASSIGN(
4526 auto channel,
4527 proxy.AcquireRfcommChannel(
4528 capture.handle,
4529 /*rx_config=*/
4530 RfcommChannel::Config{.cid = capture.channel_id,
4531 .max_information_length = kMaxFrameSize,
4532 .credits = kMaxCredits},
4533 /*tx_config*/
4534 RfcommChannel::Config{.cid = capture.channel_id,
4535 .max_information_length = kMaxFrameSize,
4536 .credits = kMaxCredits},
4537 kRfcommChannel,
4538 nullptr));
4539 EXPECT_EQ(channel.Write(capture.payload), PW_STATUS_OK);
4540 EXPECT_EQ(capture.sends_called, 1);
4541 }
4542
4543 // ########## RfcommReadTest
4544
TEST(RfcommReadTest,BasicRead)4545 TEST(RfcommReadTest, BasicRead) {
4546 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4547 []([[maybe_unused]] H4PacketWithHci&& packet) {});
4548 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4549 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4550 ProxyHost proxy = ProxyHost(
4551 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
4552
4553 struct {
4554 int rx_called = 0;
4555 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
4556 } capture;
4557
4558 constexpr uint8_t kExpectedFcs = 0xFA;
4559 constexpr uint16_t kHandle = 123;
4560 constexpr uint16_t kCid = 234;
4561 constexpr uint8_t kMaxCredits = 10;
4562 constexpr uint16_t kMaxFrameSize = 900;
4563 constexpr uint8_t kRfcommChannel = 0x03;
4564 const size_t frame_size = emboss::RfcommFrame::MinSizeInBytes() +
4565 /*payload*/ capture.expected_payload.size();
4566 const size_t acl_data_length =
4567 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + frame_size;
4568
4569 PW_TEST_ASSERT_OK_AND_ASSIGN(
4570 auto channel,
4571 proxy.AcquireRfcommChannel(
4572 kHandle,
4573 /*rx_config=*/
4574 RfcommChannel::Config{.cid = kCid,
4575 .max_information_length = kMaxFrameSize,
4576 .credits = kMaxCredits},
4577 /*tx_config*/
4578 RfcommChannel::Config{.cid = kCid,
4579 .max_information_length = kMaxFrameSize,
4580 .credits = kMaxCredits},
4581 kRfcommChannel,
4582 [&capture](pw::span<uint8_t> payload) {
4583 ++capture.rx_called;
4584 EXPECT_TRUE(std::equal(payload.begin(),
4585 payload.end(),
4586 capture.expected_payload.begin(),
4587 capture.expected_payload.end()));
4588 }));
4589
4590 std::array<uint8_t, kBFrameOverAclMinSize + frame_size> hci_arr{};
4591 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
4592
4593 PW_TEST_ASSERT_OK_AND_ASSIGN(
4594 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
4595 acl.header().handle().Write(kHandle);
4596 acl.data_total_length().Write(acl_data_length);
4597
4598 auto bframe = emboss::MakeBFrameView(acl.payload().BackingStorage().data(),
4599 acl.data_total_length().Read());
4600 bframe.pdu_length().Write(frame_size);
4601 bframe.channel_id().Write(kCid);
4602
4603 auto rfcomm = emboss::MakeRfcommFrameView(
4604 bframe.payload().BackingStorage().data(), bframe.payload().SizeInBytes());
4605 rfcomm.extended_address().Write(true);
4606 rfcomm.command_response_direction().Write(
4607 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_INITIATOR);
4608 rfcomm.channel().Write(kRfcommChannel);
4609
4610 rfcomm.control().Write(
4611 emboss::RfcommFrameType::UNNUMBERED_INFORMATION_WITH_HEADER_CHECK);
4612
4613 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
4614 rfcomm.length().Write(capture.expected_payload.size());
4615
4616 std::memcpy(rfcomm.information().BackingStorage().data(),
4617 capture.expected_payload.data(),
4618 capture.expected_payload.size());
4619 rfcomm.fcs().Write(kExpectedFcs);
4620
4621 // Send ACL data packet destined for the channel we registered.
4622 proxy.HandleH4HciFromController(std::move(h4_packet));
4623
4624 EXPECT_EQ(capture.rx_called, 1);
4625 }
4626
TEST(RfcommReadTest,ExtendedRead)4627 TEST(RfcommReadTest, ExtendedRead) {
4628 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4629 []([[maybe_unused]] H4PacketWithHci&& packet) {});
4630 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4631 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4632 ProxyHost proxy = ProxyHost(
4633 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
4634
4635 constexpr size_t kPayloadSize = 0x80;
4636 struct {
4637 int rx_called = 0;
4638 std::array<uint8_t, kPayloadSize> expected_payload = {0xAB, 0xCD, 0xEF};
4639 } capture;
4640
4641 constexpr uint8_t kExpectedFcs = 0xFA;
4642 constexpr uint16_t kHandle = 123;
4643 constexpr uint16_t kCid = 234;
4644 constexpr uint8_t kMaxCredits = 10;
4645 constexpr uint16_t kMaxFrameSize = 900;
4646 constexpr uint8_t kRfcommChannel = 0x03;
4647
4648 const size_t frame_size = emboss::RfcommFrame::MinSizeInBytes() +
4649 /*length_extended*/ 1 +
4650 /*payload*/ capture.expected_payload.size();
4651 const size_t acl_data_length =
4652 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + frame_size;
4653
4654 PW_TEST_ASSERT_OK_AND_ASSIGN(
4655 auto channel,
4656 proxy.AcquireRfcommChannel(
4657 kHandle,
4658 /*rx_config=*/
4659 RfcommChannel::Config{.cid = kCid,
4660 .max_information_length = kMaxFrameSize,
4661 .credits = kMaxCredits},
4662 /*tx_config*/
4663 RfcommChannel::Config{.cid = kCid,
4664 .max_information_length = kMaxFrameSize,
4665 .credits = kMaxCredits},
4666 kRfcommChannel,
4667 [&capture](pw::span<uint8_t> payload) {
4668 ++capture.rx_called;
4669 EXPECT_TRUE(std::equal(payload.begin(),
4670 payload.end(),
4671 capture.expected_payload.begin(),
4672 capture.expected_payload.end()));
4673 }));
4674
4675 std::array<uint8_t, kBFrameOverAclMinSize + frame_size> hci_arr{};
4676 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
4677
4678 PW_TEST_ASSERT_OK_AND_ASSIGN(
4679 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
4680 acl.header().handle().Write(kHandle);
4681 acl.data_total_length().Write(acl_data_length);
4682
4683 auto bframe = emboss::MakeBFrameView(acl.payload().BackingStorage().data(),
4684 acl.data_total_length().Read());
4685 bframe.pdu_length().Write(frame_size);
4686 bframe.channel_id().Write(kCid);
4687
4688 auto rfcomm = emboss::MakeRfcommFrameView(
4689 bframe.payload().BackingStorage().data(), bframe.payload().SizeInBytes());
4690 rfcomm.extended_address().Write(true);
4691 rfcomm.command_response_direction().Write(
4692 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_INITIATOR);
4693 rfcomm.channel().Write(kRfcommChannel);
4694
4695 rfcomm.control().Write(
4696 emboss::RfcommFrameType::UNNUMBERED_INFORMATION_WITH_HEADER_CHECK);
4697
4698 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::EXTENDED);
4699 rfcomm.length_extended().Write(capture.expected_payload.size());
4700
4701 std::memcpy(rfcomm.information().BackingStorage().data(),
4702 capture.expected_payload.data(),
4703 capture.expected_payload.size());
4704 rfcomm.fcs().Write(kExpectedFcs);
4705
4706 // Send ACL data packet destined for the channel we registered.
4707 proxy.HandleH4HciFromController(std::move(h4_packet));
4708
4709 EXPECT_EQ(capture.rx_called, 1);
4710 }
4711
TEST(RfcommReadTest,InvalidReads)4712 TEST(RfcommReadTest, InvalidReads) {
4713 struct {
4714 int rx_called = 0;
4715 int host_called = 0;
4716 } capture;
4717
4718 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
4719 [&capture]([[maybe_unused]] H4PacketWithHci&& packet) {
4720 ++capture.host_called;
4721 });
4722 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
4723 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4724 ProxyHost proxy = ProxyHost(
4725 std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
4726
4727 constexpr uint8_t kExpectedFcs = 0xFA;
4728 constexpr uint8_t kInvalidFcs = 0xFF;
4729 constexpr uint16_t kHandle = 123;
4730 constexpr uint16_t kCid = 234;
4731 constexpr uint8_t kMaxCredits = 10;
4732 constexpr uint16_t kMaxFrameSize = 900;
4733 constexpr uint8_t kRfcommChannel = 0x03;
4734 const size_t frame_size = emboss::RfcommFrame::MinSizeInBytes() +
4735 /*payload*/ 0;
4736 const size_t acl_data_length =
4737 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + frame_size;
4738
4739 PW_TEST_ASSERT_OK_AND_ASSIGN(
4740 auto channel,
4741 proxy.AcquireRfcommChannel(
4742 kHandle,
4743 /*rx_config=*/
4744 RfcommChannel::Config{.cid = kCid,
4745 .max_information_length = kMaxFrameSize,
4746 .credits = kMaxCredits},
4747 /*tx_config*/
4748 RfcommChannel::Config{.cid = kCid,
4749 .max_information_length = kMaxFrameSize,
4750 .credits = kMaxCredits},
4751 kRfcommChannel,
4752 [&capture](pw::span<uint8_t>) { ++capture.rx_called; }));
4753
4754 std::array<uint8_t, kBFrameOverAclMinSize + frame_size> hci_arr{};
4755
4756 // Construct valid packet but put invalid checksum on the end. Test that we
4757 // don't get it sent on to us.
4758 {
4759 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
4760 PW_TEST_ASSERT_OK_AND_ASSIGN(
4761 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
4762 acl.header().handle().Write(kHandle);
4763 acl.data_total_length().Write(acl_data_length);
4764
4765 auto bframe = emboss::MakeBFrameView(acl.payload().BackingStorage().data(),
4766 acl.data_total_length().Read());
4767 bframe.pdu_length().Write(frame_size);
4768 bframe.channel_id().Write(kCid);
4769
4770 auto rfcomm =
4771 emboss::MakeRfcommFrameView(bframe.payload().BackingStorage().data(),
4772 bframe.payload().SizeInBytes());
4773 rfcomm.extended_address().Write(true);
4774 rfcomm.command_response_direction().Write(
4775 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_INITIATOR);
4776 rfcomm.channel().Write(kRfcommChannel);
4777
4778 rfcomm.control().Write(
4779 emboss::RfcommFrameType::UNNUMBERED_INFORMATION_WITH_HEADER_CHECK);
4780
4781 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
4782 rfcomm.length().Write(0);
4783 rfcomm.fcs().Write(kInvalidFcs);
4784
4785 proxy.HandleH4HciFromController(std::move(h4_packet));
4786 }
4787
4788 EXPECT_EQ(capture.rx_called, 0);
4789 EXPECT_EQ(capture.host_called, 1);
4790
4791 // Construct packet with everything valid but wrong length for actual data
4792 // size. Ensure it doesn't end up being sent to our channel, but does get
4793 // forwarded to host.
4794 {
4795 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_arr};
4796
4797 PW_TEST_ASSERT_OK_AND_ASSIGN(
4798 auto acl, MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_arr));
4799 acl.header().handle().Write(kHandle);
4800 acl.data_total_length().Write(acl_data_length);
4801
4802 auto bframe = emboss::MakeBFrameView(acl.payload().BackingStorage().data(),
4803 acl.data_total_length().Read());
4804 bframe.pdu_length().Write(frame_size);
4805 bframe.channel_id().Write(kCid);
4806
4807 auto rfcomm =
4808 emboss::MakeRfcommFrameView(bframe.payload().BackingStorage().data(),
4809 bframe.payload().SizeInBytes());
4810 rfcomm.extended_address().Write(true);
4811 rfcomm.command_response_direction().Write(
4812 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_INITIATOR);
4813 rfcomm.channel().Write(kRfcommChannel);
4814
4815 rfcomm.control().Write(
4816 emboss::RfcommFrameType::UNNUMBERED_INFORMATION_WITH_HEADER_CHECK);
4817
4818 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
4819 // Invalid length.
4820 rfcomm.length().Write(1);
4821 // Can't Write FCS as emboss will assert because of invalid length. Place
4822 // manually.
4823 hci_arr[hci_arr.size() - 1] = kExpectedFcs;
4824
4825 proxy.HandleH4HciFromController(std::move(h4_packet));
4826 }
4827
4828 EXPECT_EQ(capture.rx_called, 0);
4829 EXPECT_EQ(capture.host_called, 2);
4830 }
4831
4832 // ########## ProxyHostConnectionEventTest
4833
TEST(ProxyHostConnectionEventTest,ConnectionCompletePassthroughOk)4834 TEST(ProxyHostConnectionEventTest, ConnectionCompletePassthroughOk) {
4835 size_t host_called = 0;
4836 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
4837 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4838
4839 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
4840 [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
4841 ++host_called;
4842 });
4843
4844 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4845 std::move(send_to_controller_fn),
4846 /*le_acl_credits_to_reserve=*/0);
4847
4848 PW_TEST_EXPECT_OK(
4849 SendConnectionCompleteEvent(proxy, 1, emboss::StatusCode::SUCCESS));
4850 EXPECT_EQ(host_called, 1U);
4851
4852 PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, 1));
4853 EXPECT_EQ(host_called, 2U);
4854 }
4855
TEST(ProxyHostConnectionEventTest,ConnectionCompleteWithErrorStatusPassthroughOk)4856 TEST(ProxyHostConnectionEventTest,
4857 ConnectionCompleteWithErrorStatusPassthroughOk) {
4858 size_t host_called = 0;
4859 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
4860 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4861
4862 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
4863 [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
4864 ++host_called;
4865 });
4866
4867 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4868 std::move(send_to_controller_fn),
4869 /*le_acl_credits_to_reserve=*/0);
4870
4871 PW_TEST_EXPECT_OK(SendConnectionCompleteEvent(
4872 proxy, 1, emboss::StatusCode::CONNECTION_FAILED_TO_BE_ESTABLISHED));
4873 EXPECT_EQ(host_called, 1U);
4874 }
4875
TEST(ProxyHostConnectionEventTest,LeConnectionCompletePassthroughOk)4876 TEST(ProxyHostConnectionEventTest, LeConnectionCompletePassthroughOk) {
4877 size_t host_called = 0;
4878 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
4879 []([[maybe_unused]] H4PacketWithH4&& packet) {});
4880
4881 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
4882 [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) {
4883 ++host_called;
4884 });
4885
4886 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
4887 std::move(send_to_controller_fn),
4888 /*le_acl_credits_to_reserve=*/0);
4889
4890 PW_TEST_EXPECT_OK(
4891 SendLeConnectionCompleteEvent(proxy, 1, emboss::StatusCode::SUCCESS));
4892 EXPECT_EQ(host_called, 1U);
4893 }
4894
4895 // TODO: https://pwbug.dev/360929142 - Add many more tests exercising queueing
4896 // credit-based control flow.
4897
4898 } // namespace
4899 } // namespace pw::bluetooth::proxy
4900