• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_proxy/proxy_host.h"
16 
17 #include <cstdint>
18 #include <numeric>
19 
20 #include "emboss_util.h"
21 #include "lib/stdcompat/utility.h"
22 #include "pw_bluetooth/hci_commands.emb.h"
23 #include "pw_bluetooth/hci_common.emb.h"
24 #include "pw_bluetooth/hci_events.emb.h"
25 #include "pw_bluetooth/hci_h4.emb.h"
26 #include "pw_unit_test/framework.h"  // IWYU pragma: keep
27 
28 namespace pw::bluetooth::proxy {
29 
30 namespace {
31 
32 // ########## Util functions
33 
34 // Populate passed H4 command buffer and return Emboss view on it.
35 template <typename EmbossT>
CreateAndPopulateToControllerView(H4HciPacket & h4_packet,emboss::OpCode opcode)36 EmbossT CreateAndPopulateToControllerView(H4HciPacket& h4_packet,
37                                           emboss::OpCode opcode) {
38   std::iota(h4_packet.hci_span.begin(), h4_packet.hci_span.end(), 100);
39   h4_packet.h4_type = emboss::H4PacketType::COMMAND;
40   EmbossT view = MakeEmboss<EmbossT>(h4_packet.hci_span);
41   EXPECT_TRUE(view.IsComplete());
42   view.header().opcode_enum().Write(opcode);
43   return view;
44 }
45 
46 // Return a populated H4 command buffer of a type that proxy host doesn't
47 // interact with.
PopulateNoninteractingToControllerBuffer(H4HciPacket & h4_packet)48 void PopulateNoninteractingToControllerBuffer(H4HciPacket& h4_packet) {
49   CreateAndPopulateToControllerView<emboss::InquiryCommandWriter>(
50       h4_packet, emboss::OpCode::LINK_KEY_REQUEST_REPLY);
51 }
52 
53 // Populate passed H4 event buffer and return Emboss view on it.
54 template <typename EmbossT>
CreateAndPopulateToHostEventView(H4HciPacket & h4_packet,emboss::EventCode event_code)55 EmbossT CreateAndPopulateToHostEventView(H4HciPacket& h4_packet,
56                                          emboss::EventCode event_code) {
57   std::iota(h4_packet.hci_span.begin(), h4_packet.hci_span.end(), 0x10);
58   h4_packet.h4_type = emboss::H4PacketType::EVENT;
59   EmbossT view = MakeEmboss<EmbossT>(h4_packet.hci_span);
60   view.header().event_code_enum().Write(event_code);
61   view.status().Write(emboss::StatusCode::SUCCESS);
62   EXPECT_TRUE(view.IsComplete());
63   return view;
64 }
65 
66 // Return a populated H4 event buffer of a type that proxy host doesn't interact
67 // with.
68 
CreateNonInteractingToHostBuffer(H4HciPacket & h4_packet)69 void CreateNonInteractingToHostBuffer(H4HciPacket& h4_packet) {
70   CreateAndPopulateToHostEventView<emboss::InquiryCompleteEventWriter>(
71       h4_packet, emboss::EventCode::INQUIRY_COMPLETE);
72 }
73 
74 // ########## Examples
75 
76 // Example for docs.rst.
TEST(Example,ExampleUsage)77 TEST(Example, ExampleUsage) {
78   // Populate H4 buffer to send towards controller.
79   std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes()>
80       hci_array_from_host;
81   H4HciPacket h4_packet_from_host{emboss::H4PacketType::UNKNOWN,
82                                   hci_array_from_host};
83   PopulateNoninteractingToControllerBuffer(h4_packet_from_host);
84 
85   // Populate H4 buffer to send towards host.
86   std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes() + 1>
87       hci_array_from_controller;
88   H4HciPacket h4_packet_from_controller{emboss::H4PacketType::UNKNOWN,
89                                         hci_array_from_controller};
90 
91   CreateNonInteractingToHostBuffer(h4_packet_from_controller);
92 
93   H4HciPacketSendFn containerSendToHostFn(
94       []([[maybe_unused]] H4HciPacket packet) {});
95 
96   H4HciPacketSendFn containerSendToControllerFn(
97       ([]([[maybe_unused]] H4HciPacket packet) {}));
98 
99   // DOCSTAG: [pw_bluetooth_proxy-examples-basic]
100 
101 #include "pw_bluetooth_proxy/proxy_host.h"
102 
103   // Container creates ProxyHost .
104   ProxyHost proxy = ProxyHost(std::move(containerSendToHostFn),
105                               std::move(containerSendToControllerFn),
106                               2);
107 
108   // Container passes H4 packets from host through proxy. Proxy will in turn
109   // call the container-provided `containerSendToControllerFn` to pass them on
110   // to the controller. Some packets may be modified, added, or removed.
111   proxy.HandleH4HciFromHost(h4_packet_from_host);
112 
113   // Container passes H4 packets from controller through proxy. Proxy will in
114   // turn call the container-provided `containerSendToHostFn` to pass them on to
115   // the controller. Some packets may be modified, added, or removed.
116   proxy.HandleH4HciFromController(h4_packet_from_controller);
117 
118   // DOCSTAG: [pw_bluetooth_proxy-examples-basic]
119 }
120 
121 // ########## PassthroughTest
122 
123 // Verify buffer is properly passed (contents unaltered and zero-copy).
TEST(PassthroughTest,ToControllerPassesEqualBuffer)124 TEST(PassthroughTest, ToControllerPassesEqualBuffer) {
125   std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes()> hci_arr;
126   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
127   PopulateNoninteractingToControllerBuffer(h4_packet);
128 
129   // Struct for capturing because `pw::Function` can't fit multiple captures.
130   struct {
131     // Use a copy for comparison to catch if proxy incorrectly changes the
132     // passed buffer.
133     std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes()> hci_arr;
134     H4HciPacket* h4_packet;
135     bool send_called;
136   } send_capture = {hci_arr, &h4_packet, false};
137 
138   H4HciPacketSendFn send_to_controller_fn([&send_capture](H4HciPacket packet) {
139     send_capture.send_called = true;
140     EXPECT_EQ(packet.h4_type, send_capture.h4_packet->h4_type);
141     EXPECT_TRUE(std::equal(send_capture.h4_packet->hci_span.begin(),
142                            send_capture.h4_packet->hci_span.end(),
143                            send_capture.h4_packet->hci_span.begin(),
144                            send_capture.h4_packet->hci_span.end()));
145     // Verify no copy by verifying buffer is at the same memory location.
146     EXPECT_EQ(packet.hci_span.data(), send_capture.h4_packet->hci_span.data());
147   });
148 
149   H4HciPacketSendFn send_to_host_fn([]([[maybe_unused]] H4HciPacket packet) {});
150 
151   ProxyHost proxy = ProxyHost(
152       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
153 
154   proxy.HandleH4HciFromHost(h4_packet);
155 
156   // Verify to controller callback was called.
157   EXPECT_EQ(send_capture.send_called, true);
158 }
159 
160 // Verify buffer is properly passed (contents unaltered and zero-copy).
TEST(PassthroughTest,ToHostPassesEqualBuffer)161 TEST(PassthroughTest, ToHostPassesEqualBuffer) {
162   std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()> hci_arr;
163   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
164   CreateNonInteractingToHostBuffer(h4_packet);
165 
166   // Struct for capturing because `pw::Function` can't fit multiple captures.
167   struct {
168     // Use a copy for comparison to catch if proxy incorrectly changes the
169     // passed buffer.
170     std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
171         hci_arr;
172     H4HciPacket* h4_packet;
173     bool send_called;
174   } send_capture = {hci_arr, &h4_packet, false};
175 
176   H4HciPacketSendFn send_to_host_fn([&send_capture](H4HciPacket packet) {
177     send_capture.send_called = true;
178     EXPECT_EQ(packet.h4_type, send_capture.h4_packet->h4_type);
179     EXPECT_TRUE(std::equal(send_capture.h4_packet->hci_span.begin(),
180                            send_capture.h4_packet->hci_span.end(),
181                            send_capture.h4_packet->hci_span.begin(),
182                            send_capture.h4_packet->hci_span.end()));
183     // Verify no copy by verifying buffer is at the same memory location.
184     EXPECT_EQ(packet.hci_span.data(), send_capture.h4_packet->hci_span.data());
185   });
186 
187   H4HciPacketSendFn send_to_controller_fn(
188       []([[maybe_unused]] H4HciPacket packet) {});
189 
190   ProxyHost proxy = ProxyHost(
191       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
192 
193   proxy.HandleH4HciFromController(h4_packet);
194 
195   // Verify to controller callback was called.
196   EXPECT_EQ(send_capture.send_called, true);
197 }
198 
199 // Verify a command complete event (of a type that proxy doesn't act on) is
200 // properly passed (contents unaltered and zero-copy).
TEST(PassthroughTest,ToHostPassesEqualCommandComplete)201 TEST(PassthroughTest, ToHostPassesEqualCommandComplete) {
202   std::array<
203       uint8_t,
204       emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
205       hci_arr;
206   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
207   emboss::ReadLocalVersionInfoCommandCompleteEventWriter view =
208       CreateAndPopulateToHostEventView<
209           emboss::ReadLocalVersionInfoCommandCompleteEventWriter>(
210           h4_packet, emboss::EventCode::COMMAND_COMPLETE);
211   view.command_complete().command_opcode_enum().Write(
212       emboss::OpCode::READ_LOCAL_VERSION_INFO);
213 
214   // Struct for capturing because `pw::Function` can't fit multiple captures.
215   struct {
216     std::array<
217         uint8_t,
218         emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
219         hci_arr;
220     H4HciPacket* h4_packet;
221     bool send_called;
222   } send_capture = {hci_arr, &h4_packet, false};
223 
224   H4HciPacketSendFn send_to_host_fn([&send_capture](H4HciPacket packet) {
225     send_capture.send_called = true;
226     EXPECT_EQ(packet.h4_type, send_capture.h4_packet->h4_type);
227     EXPECT_TRUE(std::equal(send_capture.h4_packet->hci_span.begin(),
228                            send_capture.h4_packet->hci_span.end(),
229                            send_capture.h4_packet->hci_span.begin(),
230                            send_capture.h4_packet->hci_span.end()));
231     // Verify no copy by verifying buffer is at the same memory location.
232     EXPECT_EQ(packet.hci_span.data(), send_capture.h4_packet->hci_span.data());
233   });
234 
235   H4HciPacketSendFn send_to_controller_fn(
236       []([[maybe_unused]] H4HciPacket packet) {});
237 
238   ProxyHost proxy = ProxyHost(
239       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
240 
241   proxy.HandleH4HciFromController(h4_packet);
242 
243   // Verify to controller callback was called.
244   EXPECT_EQ(send_capture.send_called, true);
245 }
246 
247 // ########## BadPacketTest
248 // The proxy should not affect buffers it can't process (it should just pass
249 // them on).
250 
TEST(BadPacketTest,BadH4TypeToControllerIsPassedOn)251 TEST(BadPacketTest, BadH4TypeToControllerIsPassedOn) {
252   std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes()> hci_arr;
253   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
254   PopulateNoninteractingToControllerBuffer(h4_packet);
255 
256   // Set back to an invalid type.
257   h4_packet.h4_type = emboss::H4PacketType::UNKNOWN;
258 
259   // Struct for capturing because `pw::Function` can't fit multiple captures.
260   struct {
261     // Use a copy for comparison to catch if proxy incorrectly changes the
262     // passed buffer.
263     std::array<uint8_t, emboss::InquiryCommandView::SizeInBytes()> hci_arr;
264     H4HciPacket* h4_packet;
265     bool send_called;
266   } send_capture = {hci_arr, &h4_packet, false};
267 
268   H4HciPacketSendFn send_to_controller_fn([&send_capture](H4HciPacket packet) {
269     send_capture.send_called = true;
270     EXPECT_EQ(packet.h4_type, emboss::H4PacketType::UNKNOWN);
271     EXPECT_TRUE(std::equal(send_capture.h4_packet->hci_span.begin(),
272                            send_capture.h4_packet->hci_span.end(),
273                            send_capture.h4_packet->hci_span.begin(),
274                            send_capture.h4_packet->hci_span.end()));
275     // Verify no copy by verifying buffer is at the same memory location.
276     EXPECT_EQ(packet.hci_span.data(), send_capture.h4_packet->hci_span.data());
277   });
278 
279   H4HciPacketSendFn send_to_host_fn([]([[maybe_unused]] H4HciPacket packet) {});
280 
281   ProxyHost proxy = ProxyHost(
282       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
283 
284   proxy.HandleH4HciFromHost(h4_packet);
285 
286   // Verify to controller callback was called.
287   EXPECT_EQ(send_capture.send_called, true);
288 }
289 
TEST(PBadPacketTest,BadH4TypeToHostIsPassedOn)290 TEST(PBadPacketTest, BadH4TypeToHostIsPassedOn) {
291   std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()> hci_arr;
292   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
293   CreateNonInteractingToHostBuffer(h4_packet);
294 
295   // Set back to an invalid type.
296   h4_packet.h4_type = emboss::H4PacketType::UNKNOWN;
297 
298   // Struct for capturing because `pw::Function` can't fit multiple captures.
299   struct {
300     // Use a copy for comparison to catch if proxy incorrectly changes the
301     // passed buffer.
302     std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
303         hci_arr;
304     H4HciPacket* h4_packet;
305     bool send_called;
306   } send_capture = {hci_arr, &h4_packet, false};
307 
308   H4HciPacketSendFn send_to_host_fn([&send_capture](H4HciPacket packet) {
309     send_capture.send_called = true;
310     EXPECT_EQ(packet.h4_type, emboss::H4PacketType::UNKNOWN);
311     EXPECT_TRUE(std::equal(send_capture.h4_packet->hci_span.begin(),
312                            send_capture.h4_packet->hci_span.end(),
313                            send_capture.h4_packet->hci_span.begin(),
314                            send_capture.h4_packet->hci_span.end()));
315     // Verify no copy by verifying buffer is at the same memory location.
316     EXPECT_EQ(packet.hci_span.data(), send_capture.h4_packet->hci_span.data());
317   });
318 
319   H4HciPacketSendFn send_to_controller_fn(
320       []([[maybe_unused]] H4HciPacket packet) {});
321 
322   ProxyHost proxy = ProxyHost(
323       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
324 
325   proxy.HandleH4HciFromController(h4_packet);
326 
327   // Verify to controller callback was called.
328   EXPECT_EQ(send_capture.send_called, true);
329 }
330 
TEST(BadPacketTest,EmptyBufferToControllerIsPassedOn)331 TEST(BadPacketTest, EmptyBufferToControllerIsPassedOn) {
332   std::array<uint8_t, 0> hci_arr;
333   H4HciPacket h4_packet{emboss::H4PacketType::COMMAND, hci_arr};
334 
335   bool send_called = false;
336   H4HciPacketSendFn send_to_controller_fn([&send_called](H4HciPacket packet) {
337     send_called = true;
338     EXPECT_EQ(packet.h4_type, emboss::H4PacketType::COMMAND);
339     EXPECT_TRUE(packet.hci_span.empty());
340   });
341 
342   H4HciPacketSendFn send_to_host_fn([]([[maybe_unused]] H4HciPacket packet) {});
343 
344   ProxyHost proxy = ProxyHost(
345       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
346 
347   proxy.HandleH4HciFromHost(h4_packet);
348 
349   // Verify callback was called.
350   EXPECT_EQ(send_called, true);
351 }
352 
TEST(BadPacketTest,EmptyBufferToHostIsPassedOn)353 TEST(BadPacketTest, EmptyBufferToHostIsPassedOn) {
354   std::array<uint8_t, 0> hci_arr;
355   H4HciPacket h4_packet{emboss::H4PacketType::EVENT, hci_arr};
356 
357   bool send_called = false;
358   H4HciPacketSendFn send_to_host_fn([&send_called](H4HciPacket packet) {
359     send_called = true;
360     EXPECT_EQ(packet.h4_type, emboss::H4PacketType::EVENT);
361     EXPECT_TRUE(packet.hci_span.empty());
362   });
363 
364   H4HciPacketSendFn send_to_controller_fn(
365       []([[maybe_unused]] H4HciPacket packet) {});
366 
367   ProxyHost proxy = ProxyHost(
368       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
369 
370   proxy.HandleH4HciFromController(h4_packet);
371 
372   // Verify callback was called.
373   EXPECT_EQ(send_called, true);
374 }
375 
TEST(BadPacketTest,TooShortEventToHostIsPassOn)376 TEST(BadPacketTest, TooShortEventToHostIsPassOn) {
377   std::array<uint8_t, emboss::InquiryCompleteEventView::SizeInBytes()>
378       valid_hci_arr;
379   H4HciPacket valid_packet{emboss::H4PacketType::UNKNOWN, valid_hci_arr};
380   CreateNonInteractingToHostBuffer(valid_packet);
381 
382   // Create packet for sending whose span size is one less than a valid command
383   // complete event.
384   H4HciPacket h4_packet{valid_packet.h4_type,
385                         valid_packet.hci_span.subspan(
386                             0, emboss::EventHeaderView::SizeInBytes() - 1)};
387 
388   // Struct for capturing because `pw::Function` can't fit multiple captures.
389   struct {
390     std::array<uint8_t, emboss::EventHeaderView::SizeInBytes() - 1> hci_arr;
391     bool send_called;
392   } send_capture;
393   // Copy valid event into a short_array whose size is one less than a valid
394   // EventHeader.
395   std::copy(h4_packet.hci_span.begin(),
396             h4_packet.hci_span.end(),
397             send_capture.hci_arr.begin());
398   send_capture.send_called = false;
399 
400   H4HciPacketSendFn send_to_host_fn([&send_capture](H4HciPacket packet) {
401     send_capture.send_called = true;
402     EXPECT_TRUE(std::equal(packet.hci_span.begin(),
403                            packet.hci_span.end(),
404                            send_capture.hci_arr.begin(),
405                            send_capture.hci_arr.end()));
406   });
407 
408   H4HciPacketSendFn send_to_controller_fn(
409       []([[maybe_unused]] H4HciPacket packet) {});
410 
411   ProxyHost proxy = ProxyHost(
412       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
413 
414   proxy.HandleH4HciFromController(h4_packet);
415 
416   // Verify callback was called.
417   EXPECT_EQ(send_capture.send_called, true);
418 }
419 
TEST(BadPacketTest,TooShortCommandCompleteEventToHost)420 TEST(BadPacketTest, TooShortCommandCompleteEventToHost) {
421   std::array<
422       uint8_t,
423       emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes()>
424       valid_hci_arr;
425   H4HciPacket valid_packet{emboss::H4PacketType::UNKNOWN, valid_hci_arr};
426   emboss::ReadLocalVersionInfoCommandCompleteEventWriter view =
427       CreateAndPopulateToHostEventView<
428           emboss::ReadLocalVersionInfoCommandCompleteEventWriter>(
429           valid_packet, emboss::EventCode::COMMAND_COMPLETE);
430   view.command_complete().command_opcode_enum().Write(
431       emboss::OpCode::READ_LOCAL_VERSION_INFO);
432 
433   // Create packet for sending whose span size is one less than a valid command
434   // complete event.
435   H4HciPacket h4_packet{
436       valid_packet.h4_type,
437       valid_packet.hci_span.subspan(
438           0,
439           emboss::ReadLocalVersionInfoCommandCompleteEventWriter::
440                   SizeInBytes() -
441               1)};
442 
443   // Struct for capturing because `pw::Function` capture can't fit multiple
444   // fields .
445   struct {
446     std::array<
447         uint8_t,
448         emboss::ReadLocalVersionInfoCommandCompleteEventWriter::SizeInBytes() -
449             1>
450         hci_arr;
451     bool send_called;
452   } send_capture;
453   std::copy(h4_packet.hci_span.begin(),
454             h4_packet.hci_span.end(),
455             send_capture.hci_arr.begin());
456   send_capture.send_called = false;
457 
458   H4HciPacketSendFn send_to_host_fn([&send_capture](H4HciPacket packet) {
459     send_capture.send_called = true;
460     EXPECT_TRUE(std::equal(packet.hci_span.begin(),
461                            packet.hci_span.end(),
462                            send_capture.hci_arr.begin(),
463                            send_capture.hci_arr.end()));
464   });
465 
466   H4HciPacketSendFn send_to_controller_fn(
467       []([[maybe_unused]] H4HciPacket packet) {});
468 
469   ProxyHost proxy = ProxyHost(
470       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
471 
472   proxy.HandleH4HciFromController(h4_packet);
473 
474   // Verify callback was called.
475   EXPECT_EQ(send_capture.send_called, true);
476 }
477 
478 // ########## ReserveLeAclCredits Tests
479 
480 // Proxy Host should reserve requested ACL LE credits from controller's ACL LE
481 // credits when using LEReadBufferSizeV1 command.
TEST(ReserveLeAclCredits,ProxyCreditsReserveCreditsWithLEReadBufferSizeV1)482 TEST(ReserveLeAclCredits, ProxyCreditsReserveCreditsWithLEReadBufferSizeV1) {
483   std::array<
484       uint8_t,
485       emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
486       hci_arr;
487   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
488   emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
489       CreateAndPopulateToHostEventView<
490           emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
491           h4_packet, emboss::EventCode::COMMAND_COMPLETE);
492   view.command_complete().command_opcode_enum().Write(
493       emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
494   view.total_num_le_acl_data_packets().Write(10);
495 
496   bool send_called = false;
497   H4HciPacketSendFn send_to_host_fn([&send_called](H4HciPacket h4_packet) {
498     send_called = true;
499     emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
500         MakeEmboss<emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
501             h4_packet.hci_span);
502     // Should reserve 2 credits from original total of 10 (so 8 left for host).
503     EXPECT_EQ(view.total_num_le_acl_data_packets().Read(), 8);
504   });
505 
506   H4HciPacketSendFn send_to_controller_fn(
507       []([[maybe_unused]] H4HciPacket packet) {});
508 
509   ProxyHost proxy = ProxyHost(
510       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
511 
512   proxy.HandleH4HciFromController(h4_packet);
513 
514   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
515 
516   EXPECT_TRUE(proxy.HasSendAclCapability());
517 
518   // Verify to controller callback was called.
519   EXPECT_EQ(send_called, true);
520 }
521 
522 // Proxy Host should reserve requested ACL LE credits from controller's ACL LE
523 // credits when using LEReadBufferSizeV2 command.
TEST(ReserveLeAclCredits,ProxyCreditsReserveCreditsWithLEReadBufferSizeV2)524 TEST(ReserveLeAclCredits, ProxyCreditsReserveCreditsWithLEReadBufferSizeV2) {
525   std::array<
526       uint8_t,
527       emboss::LEReadBufferSizeV2CommandCompleteEventWriter::SizeInBytes()>
528       hci_arr;
529   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
530   emboss::LEReadBufferSizeV2CommandCompleteEventWriter view =
531       CreateAndPopulateToHostEventView<
532           emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
533           h4_packet, emboss::EventCode::COMMAND_COMPLETE);
534   view.command_complete().command_opcode_enum().Write(
535       emboss::OpCode::LE_READ_BUFFER_SIZE_V2);
536   view.total_num_le_acl_data_packets().Write(10);
537 
538   bool send_called = false;
539   H4HciPacketSendFn send_to_host_fn([&send_called](H4HciPacket h4_packet) {
540     send_called = true;
541     emboss::LEReadBufferSizeV2CommandCompleteEventWriter view =
542         MakeEmboss<emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
543             h4_packet.hci_span);
544     // Should reserve 2 credits from original total of 10 (so 8 left for host).
545     EXPECT_EQ(view.total_num_le_acl_data_packets().Read(), 8);
546   });
547 
548   H4HciPacketSendFn send_to_controller_fn(
549       []([[maybe_unused]] H4HciPacket packet) {});
550 
551   ProxyHost proxy = ProxyHost(
552       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
553 
554   proxy.HandleH4HciFromController(h4_packet);
555 
556   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 2);
557 
558   EXPECT_TRUE(proxy.HasSendAclCapability());
559 
560   // Verify to controller callback was called.
561   EXPECT_EQ(send_called, true);
562 }
563 
564 // If controller provides less than wanted credits, we should reserve that
565 // smaller amount.
TEST(ReserveLeAclCredits,ProxyCreditsCappedByControllerCredits)566 TEST(ReserveLeAclCredits, ProxyCreditsCappedByControllerCredits) {
567   std::array<
568       uint8_t,
569       emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
570       hci_arr;
571   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
572   emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
573       CreateAndPopulateToHostEventView<
574           emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
575           h4_packet, emboss::EventCode::COMMAND_COMPLETE);
576   view.command_complete().command_opcode_enum().Write(
577       emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
578   view.total_num_le_acl_data_packets().Write(5);
579 
580   bool send_called = false;
581   H4HciPacketSendFn send_to_host_fn([&send_called](H4HciPacket h4_packet) {
582     send_called = true;
583     // We want 7, but can reserve only 5 from original 5 (so 0 left for host).
584     emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
585         MakeEmboss<emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
586             h4_packet.hci_span);
587     EXPECT_EQ(view.total_num_le_acl_data_packets().Read(), 0);
588   });
589 
590   H4HciPacketSendFn send_to_controller_fn(
591       []([[maybe_unused]] H4HciPacket packet) {});
592 
593   ProxyHost proxy = ProxyHost(
594       std::move(send_to_host_fn), std::move(send_to_controller_fn), 7);
595 
596   proxy.HandleH4HciFromController(h4_packet);
597 
598   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 5);
599 
600   // Verify to controller callback was called.
601   EXPECT_EQ(send_called, true);
602 }
603 
604 // Proxy Host can reserve zero credits from controller's ACL LE credits.
TEST(ReserveLeAclCredits,ProxyCreditsReserveZeroCredits)605 TEST(ReserveLeAclCredits, ProxyCreditsReserveZeroCredits) {
606   std::array<
607       uint8_t,
608       emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
609       hci_arr;
610   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
611   emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
612       CreateAndPopulateToHostEventView<
613           emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
614           h4_packet, emboss::EventCode::COMMAND_COMPLETE);
615   view.command_complete().command_opcode_enum().Write(
616       emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
617   view.total_num_le_acl_data_packets().Write(10);
618 
619   bool send_called = false;
620   H4HciPacketSendFn send_to_host_fn([&send_called](H4HciPacket h4_packet) {
621     send_called = true;
622     emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
623         MakeEmboss<emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
624             h4_packet.hci_span);
625     // Should reserve 0 credits from original total of 10 (so 10 left for host).
626     EXPECT_EQ(view.total_num_le_acl_data_packets().Read(), 10);
627   });
628 
629   H4HciPacketSendFn send_to_controller_fn(
630       []([[maybe_unused]] H4HciPacket packet) {});
631 
632   ProxyHost proxy = ProxyHost(
633       std::move(send_to_host_fn), std::move(send_to_controller_fn), 0);
634 
635   proxy.HandleH4HciFromController(h4_packet);
636 
637   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
638 
639   EXPECT_FALSE(proxy.HasSendAclCapability());
640 
641   // Verify to controller callback was called.
642   EXPECT_EQ(send_called, true);
643 }
644 
645 // If controller has no credits, proxy should reserve none.
TEST(ReserveLeAclPackets,ProxyCreditsZeroWhenHostCreditsZero)646 TEST(ReserveLeAclPackets, ProxyCreditsZeroWhenHostCreditsZero) {
647   std::array<
648       uint8_t,
649       emboss::LEReadBufferSizeV1CommandCompleteEventWriter::SizeInBytes()>
650       hci_arr;
651   H4HciPacket h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
652   emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
653       CreateAndPopulateToHostEventView<
654           emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
655           h4_packet, emboss::EventCode::COMMAND_COMPLETE);
656   view.command_complete().command_opcode_enum().Write(
657       emboss::OpCode::LE_READ_BUFFER_SIZE_V1);
658   view.total_num_le_acl_data_packets().Write(0);
659 
660   bool send_called = false;
661   H4HciPacketSendFn send_to_host_fn([&send_called](H4HciPacket h4_packet) {
662     send_called = true;
663     emboss::LEReadBufferSizeV1CommandCompleteEventWriter view =
664         MakeEmboss<emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(
665             h4_packet.hci_span);
666     // Should reserve 0 credit from original total of 0 (so 0 left for host).
667     EXPECT_EQ(view.total_num_le_acl_data_packets().Read(), 0);
668   });
669 
670   H4HciPacketSendFn send_to_controller_fn(
671       []([[maybe_unused]] H4HciPacket packet) {});
672 
673   ProxyHost proxy = ProxyHost(
674       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
675 
676   proxy.HandleH4HciFromController(h4_packet);
677 
678   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
679 
680   EXPECT_TRUE(proxy.HasSendAclCapability());
681 
682   // Verify to controller callback was called.
683   EXPECT_EQ(send_called, true);
684 }
685 
TEST(ReserveLeAclPackets,ProxyCreditsZeroWhenNotInitialized)686 TEST(ReserveLeAclPackets, ProxyCreditsZeroWhenNotInitialized) {
687   H4HciPacketSendFn send_to_host_fn([]([[maybe_unused]] H4HciPacket packet) {});
688 
689   H4HciPacketSendFn send_to_controller_fn(
690       []([[maybe_unused]] H4HciPacket packet) {});
691 
692   ProxyHost proxy = ProxyHost(
693       std::move(send_to_host_fn), std::move(send_to_controller_fn), 2);
694 
695   EXPECT_EQ(proxy.GetNumFreeLeAclPackets(), 0);
696 
697   EXPECT_TRUE(proxy.HasSendAclCapability());
698 }
699 
700 }  // namespace
701 }  // namespace pw::bluetooth::proxy
702