1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include <cstdint>
16
17 #include "pw_bluetooth/emboss_util.h"
18 #include "pw_bluetooth/hci_data.emb.h"
19 #include "pw_bluetooth/hci_h4.emb.h"
20 #include "pw_bluetooth/l2cap_frames.emb.h"
21 #include "pw_bluetooth/rfcomm_frames.emb.h"
22 #include "pw_bluetooth_proxy/h4_packet.h"
23 #include "pw_bluetooth_proxy/proxy_host.h"
24 #include "pw_bluetooth_proxy_private/test_utils.h"
25 #include "pw_containers/flat_map.h"
26 #include "pw_status/status.h"
27 #include "pw_status/try.h"
28 #include "pw_unit_test/framework.h"
29
30 namespace pw::bluetooth::proxy {
31 namespace {
32
33 using containers::FlatMap;
34
35 // ########## RfcommWriteTest
36
37 class RfcommWriteTest : public ProxyHostTest {};
38
39 // Construct and send an RFCOMM frame from controller->host.
SendRfcommFromController(ProxyHost & proxy,RfcommParameters params,uint8_t fcs,std::optional<uint8_t> credits,pw::span<uint8_t> payload)40 Status SendRfcommFromController(ProxyHost& proxy,
41 RfcommParameters params,
42 uint8_t fcs,
43 std::optional<uint8_t> credits,
44 pw::span<uint8_t> payload) {
45 constexpr size_t kMaxShortLength = 0x7f;
46 const size_t credits_field_size = credits.has_value() ? 1 : 0;
47 const bool uses_extended_length = payload.size() > kMaxShortLength;
48 const size_t length_extended_size = uses_extended_length ? 1 : 0;
49 const size_t frame_size = emboss::RfcommFrame::MinSizeInBytes() +
50 length_extended_size + credits_field_size +
51 payload.size();
52
53 PW_TRY_ASSIGN(BFrameWithStorage bframe,
54 SetupBFrame(params.handle, params.rx_config.cid, frame_size));
55
56 auto rfcomm = emboss::MakeRfcommFrameView(
57 bframe.writer.payload().BackingStorage().data(),
58 bframe.writer.payload().SizeInBytes());
59 rfcomm.extended_address().Write(true);
60 rfcomm.command_response_direction().Write(
61 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_INITIATOR);
62 rfcomm.channel().Write(params.rfcomm_channel);
63
64 if (!uses_extended_length) {
65 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
66 rfcomm.length().Write(payload.size());
67 } else {
68 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::EXTENDED);
69 rfcomm.length_extended().Write(payload.size());
70 }
71
72 if (credits.has_value()) {
73 rfcomm.control().Write(
74 emboss::RfcommFrameType::
75 UNNUMBERED_INFORMATION_WITH_HEADER_CHECK_AND_POLL_FINAL);
76 rfcomm.credits().Write(*credits);
77 } else {
78 rfcomm.control().Write(
79 emboss::RfcommFrameType::UNNUMBERED_INFORMATION_WITH_HEADER_CHECK);
80 }
81
82 EXPECT_EQ(rfcomm.information().SizeInBytes(), payload.size());
83 EXPECT_TRUE(TryToCopyToEmbossStruct(/*emboss_dest=*/rfcomm.information(),
84 /*src=*/payload));
85 rfcomm.fcs().Write(fcs);
86 auto hci_span = bframe.acl.hci_span();
87 H4PacketWithHci packet{emboss::H4PacketType::ACL_DATA, hci_span};
88
89 proxy.HandleH4HciFromController(std::move(packet));
90
91 return OkStatus();
92 }
93
TEST_F(RfcommWriteTest,BasicWrite)94 TEST_F(RfcommWriteTest, BasicWrite) {
95 struct {
96 int sends_called = 0;
97 // First four bits 0x0 encode PB & BC flags
98 uint16_t handle = 0x0ACB;
99 // Length of L2CAP PDU
100 uint16_t acl_data_total_length = 0x000C;
101 // L2CAP header PDU length field
102 uint16_t pdu_length = 0x0008;
103 // Random CID
104 uint16_t channel_id = 0x1234;
105 // RFCOMM header
106 std::array<uint8_t, 3> rfcomm_header = {0x19, 0xFF, 0x07};
107 uint8_t rfcomm_credits = 0;
108 // RFCOMM information payload
109 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
110 uint8_t rfcomm_fcs = 0x49;
111
112 // Built from the preceding values in little endian order (except payload in
113 // big endian).
114 std::array<uint8_t, 16> expected_hci_packet = {0xCB,
115 0x0A,
116 0x0C,
117 0x00,
118 0x08,
119 0x00,
120 0x34,
121 0x12,
122 // RFCOMM header
123 0x19,
124 0xFF,
125 0x07,
126 0x00,
127 0xAB,
128 0xCD,
129 0xEF,
130 // FCS
131 0x49};
132 } capture;
133
134 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
135 []([[maybe_unused]] H4PacketWithHci&& packet) {});
136 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
137 [&capture](H4PacketWithH4&& packet) {
138 ++capture.sends_called;
139 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
140 EXPECT_EQ(packet.GetHciSpan().size(),
141 capture.expected_hci_packet.size());
142 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
143 packet.GetHciSpan().end(),
144 capture.expected_hci_packet.begin(),
145 capture.expected_hci_packet.end()));
146 PW_TEST_ASSERT_OK_AND_ASSIGN(
147 auto acl,
148 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
149 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
150 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
151 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
152 EXPECT_EQ(acl.header().broadcast_flag().Read(),
153 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
154 EXPECT_EQ(acl.data_total_length().Read(),
155 capture.acl_data_total_length);
156 emboss::BFrameView bframe = emboss::BFrameView(
157 acl.payload().BackingStorage().data(), acl.SizeInBytes());
158 EXPECT_EQ(bframe.pdu_length().Read(), capture.pdu_length);
159 EXPECT_EQ(bframe.channel_id().Read(), capture.channel_id);
160 EXPECT_TRUE(std::equal(bframe.payload().BackingStorage().begin(),
161 bframe.payload().BackingStorage().begin() +
162 capture.rfcomm_header.size(),
163 capture.rfcomm_header.begin(),
164 capture.rfcomm_header.end()));
165 auto rfcomm = emboss::MakeRfcommFrameView(
166 bframe.payload().BackingStorage().data(),
167 bframe.payload().SizeInBytes());
168 EXPECT_TRUE(rfcomm.Ok());
169 EXPECT_EQ(rfcomm.credits().Read(), capture.rfcomm_credits);
170
171 for (size_t i = 0; i < 3; ++i) {
172 EXPECT_EQ(rfcomm.information()[i].Read(), capture.payload[i]);
173 }
174
175 EXPECT_EQ(rfcomm.fcs().Read(), capture.rfcomm_fcs);
176 });
177
178 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
179 std::move(send_to_controller_fn),
180 /*le_acl_credits_to_reserve=*/0,
181 /*br_edr_acl_credits_to_reserve=*/1);
182 // Allow proxy to reserve 1 credit.
183 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
184
185 RfcommParameters params = {.handle = capture.handle,
186 .tx_config = {
187 .cid = capture.channel_id,
188 }};
189 RfcommChannel channel = BuildRfcomm(proxy, params);
190 PW_TEST_EXPECT_OK(
191 channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
192 EXPECT_EQ(capture.sends_called, 1);
193 }
194
TEST_F(RfcommWriteTest,ExtendedWrite)195 TEST_F(RfcommWriteTest, ExtendedWrite) {
196 constexpr size_t kPayloadSize = 0x80;
197 struct {
198 int sends_called = 0;
199 // First four bits 0x0 encode PB & BC flags
200 uint16_t handle = 0x0ACB;
201 // Length of L2CAP PDU
202 uint16_t acl_data_total_length = 0x008A;
203 // L2CAP header PDU length field
204 uint16_t pdu_length = 0x0086;
205 // Random CID
206 uint16_t channel_id = 0x1234;
207 // RFCOMM header
208 std::array<uint8_t, 4> rfcomm_header = {0x19, 0xFF, 0x00, 0x01};
209 uint8_t rfcomm_credits = 0;
210 // RFCOMM information payload
211 std::array<uint8_t, kPayloadSize> payload = {
212 0xAB,
213 0xCD,
214 0xEF,
215 };
216 uint8_t rfcomm_fcs = 0x49;
217
218 // Built from the preceding values in little endian order (except payload in
219 // big endian).
220 std::array<uint8_t, kPayloadSize + 14> expected_hci_packet = {
221 0xCB,
222 0x0A,
223 0x8A,
224 0x00,
225 0x86,
226 0x00,
227 0x34,
228 0x12,
229 // RFCOMM header
230 0x19,
231 0xFF,
232 0x00,
233 0x01,
234 0x00,
235 0xAB,
236 0xCD,
237 0xEF,
238 };
239 } capture;
240
241 // FCS
242 capture.expected_hci_packet[capture.expected_hci_packet.size() - 1] =
243 capture.rfcomm_fcs;
244
245 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
246 []([[maybe_unused]] H4PacketWithHci&& packet) {});
247 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
248 [&capture](H4PacketWithH4&& packet) {
249 ++capture.sends_called;
250 EXPECT_EQ(packet.GetH4Type(), emboss::H4PacketType::ACL_DATA);
251 EXPECT_EQ(packet.GetHciSpan().size(),
252 capture.expected_hci_packet.size());
253 EXPECT_TRUE(std::equal(packet.GetHciSpan().begin(),
254 packet.GetHciSpan().end(),
255 capture.expected_hci_packet.begin(),
256 capture.expected_hci_packet.end()));
257 PW_TEST_ASSERT_OK_AND_ASSIGN(
258 auto acl,
259 MakeEmbossView<emboss::AclDataFrameView>(packet.GetHciSpan()));
260 EXPECT_EQ(acl.header().handle().Read(), capture.handle);
261 EXPECT_EQ(acl.header().packet_boundary_flag().Read(),
262 emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
263 EXPECT_EQ(acl.header().broadcast_flag().Read(),
264 emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
265 EXPECT_EQ(acl.data_total_length().Read(),
266 capture.acl_data_total_length);
267 emboss::BFrameView bframe = emboss::BFrameView(
268 acl.payload().BackingStorage().data(), acl.SizeInBytes());
269 EXPECT_EQ(bframe.pdu_length().Read(), capture.pdu_length);
270 EXPECT_EQ(bframe.channel_id().Read(), capture.channel_id);
271 EXPECT_TRUE(std::equal(bframe.payload().BackingStorage().begin(),
272 bframe.payload().BackingStorage().begin() +
273 capture.rfcomm_header.size(),
274 capture.rfcomm_header.begin(),
275 capture.rfcomm_header.end()));
276 auto rfcomm = emboss::MakeRfcommFrameView(
277 bframe.payload().BackingStorage().data(),
278 bframe.payload().SizeInBytes());
279 EXPECT_TRUE(rfcomm.Ok());
280 EXPECT_EQ(rfcomm.credits().Read(), capture.rfcomm_credits);
281
282 for (size_t i = 0; i < 3; ++i) {
283 EXPECT_EQ(rfcomm.information()[i].Read(), capture.payload[i]);
284 }
285
286 EXPECT_EQ(rfcomm.fcs().Read(), capture.rfcomm_fcs);
287 });
288
289 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
290 std::move(send_to_controller_fn),
291 /*le_acl_credits_to_reserve=*/0,
292 /*br_edr_acl_credits_to_reserve=*/1);
293 // Allow proxy to reserve 1 credit.
294 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 1));
295
296 RfcommParameters params = {.handle = capture.handle,
297 .tx_config = {
298 .cid = capture.channel_id,
299 }};
300 RfcommChannel channel = BuildRfcomm(proxy, params);
301 PW_TEST_EXPECT_OK(
302 channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
303 EXPECT_EQ(capture.sends_called, 1);
304 }
305
TEST_F(RfcommWriteTest,MixedLengthWrites)306 TEST_F(RfcommWriteTest, MixedLengthWrites) {
307 constexpr size_t kPayload1Size = 0x80;
308 constexpr size_t kPayload2Size = 0x3;
309 struct {
310 int sends_called = 0;
311 uint16_t handle = 0x0ACB;
312 // Random CID
313 uint16_t channel_id = 0x1234;
314 // RFCOMM information payload
315 std::array<uint8_t, kPayload1Size> payload = {
316 0xAB,
317 0xCD,
318 0xEF,
319 };
320 } capture;
321
322 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
323 [](H4PacketWithHci&&) {});
324 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
325 [&capture](H4PacketWithH4&&) { ++capture.sends_called; });
326
327 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
328 std::move(send_to_controller_fn),
329 /*le_acl_credits_to_reserve=*/0,
330 /*br_edr_acl_credits_to_reserve=*/2);
331 // Allow proxy to reserve 2 credits.
332 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(proxy, 2));
333
334 RfcommParameters params = {.handle = capture.handle,
335 .tx_config = {
336 .cid = capture.channel_id,
337 }};
338 RfcommChannel channel = BuildRfcomm(proxy, params);
339 PW_TEST_EXPECT_OK(
340 channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
341 PW_TEST_EXPECT_OK(channel
342 .Write(MultiBufFromSpan(
343 pw::span(capture.payload).subspan(kPayload2Size)))
344 .status);
345 EXPECT_EQ(capture.sends_called, 2);
346 }
347
TEST_F(RfcommWriteTest,WriteFlowControl)348 TEST_F(RfcommWriteTest, WriteFlowControl) {
349 struct {
350 int sends_called = 0;
351 int queue_unblocked = 0;
352 // RFCOMM information payload
353 std::array<uint8_t, 3> payload = {0xAB, 0xCD, 0xEF};
354 } capture;
355
356 pw::Function<void(H4PacketWithHci && packet)> send_to_host_fn(
357 [](H4PacketWithHci&&) {});
358 pw::Function<void(H4PacketWithH4 && packet)> send_to_controller_fn(
359 [&capture](H4PacketWithH4&&) { ++capture.sends_called; });
360 ChannelEventCallback event_fn([&capture](L2capChannelEvent event) {
361 if (event == L2capChannelEvent::kWriteAvailable) {
362 ++capture.queue_unblocked;
363 }
364 });
365
366 ProxyHost proxy = ProxyHost(
367 std::move(send_to_host_fn),
368 std::move(send_to_controller_fn),
369 /*le_acl_credits_to_reserve=*/0,
370 /*br_edr_acl_credits_to_reserve=*/RfcommChannel::QueueCapacity() + 1);
371 // Start with plenty of ACL credits to test RFCOMM logic.
372 PW_TEST_EXPECT_OK(SendReadBufferResponseFromController(
373 proxy, RfcommChannel::QueueCapacity() + 1));
374
375 RfcommParameters params = {.tx_config = {
376 .cid = 123,
377 .credits = 0,
378 }};
379 RfcommChannel channel = BuildRfcomm(proxy,
380 params,
381 /*receive_fn=*/nullptr,
382 /*event_fn=*/std::move(event_fn));
383
384 // Writes while queue has space will return Ok. No RFCOMM credits yet though
385 // so no sends complete.
386 PW_TEST_EXPECT_OK(
387 channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status);
388 EXPECT_EQ(capture.sends_called, 0);
389 EXPECT_EQ(capture.queue_unblocked, 0);
390
391 // Provide a credit
392 constexpr uint8_t kExpectedFcs = 0xE6;
393 PW_TEST_EXPECT_OK(SendRfcommFromController(
394 proxy, params, kExpectedFcs, /*credits=*/1, /*payload=*/{}));
395 EXPECT_EQ(capture.queue_unblocked, 0);
396 EXPECT_EQ(capture.sends_called, 1);
397
398 // Now fill up queue
399 uint16_t queued = 0;
400 while (true) {
401 if (const auto status =
402 channel.Write(MultiBufFromSpan(pw::span(capture.payload))).status;
403 status == Status::Unavailable()) {
404 break;
405 }
406 ++queued;
407 }
408
409 // Unblock queue with ACL and RFCOMM credits
410 PW_TEST_EXPECT_OK(SendNumberOfCompletedPackets(
411 proxy, FlatMap<uint16_t, uint16_t, 1>({{{params.handle, queued}}})));
412 PW_TEST_EXPECT_OK(SendRfcommFromController(
413 proxy, params, kExpectedFcs, /*credits=*/queued, /*payload=*/{}));
414
415 EXPECT_EQ(capture.sends_called, queued + 1);
416 EXPECT_EQ(capture.queue_unblocked, 1);
417 }
418
419 // ########## RfcommReadTest
420
421 class RfcommReadTest : public ProxyHostTest {};
422
TEST_F(RfcommReadTest,BasicRead)423 TEST_F(RfcommReadTest, BasicRead) {
424 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
425 []([[maybe_unused]] H4PacketWithHci&& packet) {});
426 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
427 []([[maybe_unused]] H4PacketWithH4&& packet) {});
428 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
429 std::move(send_to_controller_fn),
430 /*le_acl_credits_to_reserve=*/0,
431 /*br_edr_acl_credits_to_reserve=*/0);
432
433 struct {
434 int rx_called = 0;
435 std::array<uint8_t, 3> expected_payload = {0xAB, 0xCD, 0xEF};
436 } capture;
437
438 constexpr uint8_t kExpectedFcs = 0xFA;
439
440 RfcommParameters params = {};
441 RfcommChannel channel = BuildRfcomm(
442 proxy,
443 params,
444 /*receive_fn=*/[&capture](multibuf::MultiBuf&& buffer) {
445 ++capture.rx_called;
446 std::optional<pw::ByteSpan> payload = buffer.ContiguousSpan();
447 ConstByteSpan expected_bytes = as_bytes(span(
448 capture.expected_payload.data(), capture.expected_payload.size()));
449 ASSERT_TRUE(payload.has_value());
450 EXPECT_TRUE(std::equal(payload->begin(),
451 payload->end(),
452 expected_bytes.begin(),
453 expected_bytes.end()));
454 });
455
456 PW_TEST_EXPECT_OK(SendRfcommFromController(proxy,
457 params,
458 kExpectedFcs,
459 /*credits=*/std::nullopt,
460 capture.expected_payload));
461 EXPECT_EQ(capture.rx_called, 1);
462 }
463
TEST_F(RfcommReadTest,ExtendedRead)464 TEST_F(RfcommReadTest, ExtendedRead) {
465 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
466 []([[maybe_unused]] H4PacketWithHci&& packet) {});
467 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
468 []([[maybe_unused]] H4PacketWithH4&& packet) {});
469 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
470 std::move(send_to_controller_fn),
471 /*le_acl_credits_to_reserve=*/0,
472 /*br_edr_acl_credits_to_reserve=*/0);
473
474 constexpr size_t kPayloadSize = 0x80;
475 struct {
476 int rx_called = 0;
477 std::array<uint8_t, kPayloadSize> expected_payload = {0xAB, 0xCD, 0xEF};
478 } capture;
479
480 constexpr uint8_t kExpectedFcs = 0xFA;
481
482 RfcommParameters params = {};
483 RfcommChannel channel = BuildRfcomm(
484 proxy,
485 params,
486 /*receive_fn=*/
487 [&capture](multibuf::MultiBuf&& buffer) {
488 ++capture.rx_called;
489 std::optional<pw::ByteSpan> payload = buffer.ContiguousSpan();
490 ConstByteSpan expected_bytes = as_bytes(span(
491 capture.expected_payload.data(), capture.expected_payload.size()));
492 ASSERT_TRUE(payload.has_value());
493 EXPECT_TRUE(std::equal(payload->begin(),
494 payload->end(),
495 expected_bytes.begin(),
496 expected_bytes.end()));
497 });
498 PW_TEST_EXPECT_OK(SendRfcommFromController(proxy,
499 params,
500 kExpectedFcs,
501 /*credits=*/std::nullopt,
502 capture.expected_payload));
503
504 EXPECT_EQ(capture.rx_called, 1);
505 }
506
TEST_F(RfcommReadTest,InvalidReads)507 TEST_F(RfcommReadTest, InvalidReads) {
508 struct {
509 int rx_called = 0;
510 int host_called = 0;
511 } capture;
512
513 pw::Function<void(H4PacketWithHci && packet)>&& send_to_host_fn(
514 [&capture]([[maybe_unused]] H4PacketWithHci&& packet) {
515 ++capture.host_called;
516 });
517 pw::Function<void(H4PacketWithH4 && packet)>&& send_to_controller_fn(
518 []([[maybe_unused]] H4PacketWithH4&& packet) {});
519 ProxyHost proxy = ProxyHost(std::move(send_to_host_fn),
520 std::move(send_to_controller_fn),
521 /*le_acl_credits_to_reserve=*/0,
522 /*br_edr_acl_credits_to_reserve=*/0);
523
524 constexpr uint8_t kExpectedFcs = 0xFA;
525 constexpr uint8_t kInvalidFcs = 0xFF;
526
527 RfcommParameters params = {};
528 RfcommChannel channel = BuildRfcomm(
529 proxy,
530 params,
531 /*receive_fn=*/
532 [&capture](multibuf::MultiBuf&&) { ++capture.rx_called; },
533 /*event_fn=*/nullptr);
534
535 // Construct valid packet but put invalid checksum on the end. Test that we
536 // don't get it sent on to us.
537 PW_TEST_EXPECT_OK(SendRfcommFromController(proxy,
538 params,
539 kInvalidFcs,
540 /*credits=*/std::nullopt,
541 /*payload=*/{}));
542 EXPECT_EQ(capture.rx_called, 0);
543 EXPECT_EQ(capture.host_called, 1);
544
545 // Construct packet with everything valid but wrong length for actual data
546 // size. Ensure it doesn't end up being sent to our channel, but does get
547 // forwarded to host.
548 {
549 PW_TEST_ASSERT_OK_AND_ASSIGN(
550 BFrameWithStorage bframe,
551 SetupBFrame(params.handle,
552 params.rx_config.cid,
553 emboss::RfcommFrame::MinSizeInBytes()));
554
555 auto rfcomm = emboss::MakeRfcommFrameView(
556 bframe.writer.payload().BackingStorage().data(),
557 bframe.writer.payload().SizeInBytes());
558 rfcomm.extended_address().Write(true);
559 rfcomm.command_response_direction().Write(
560 emboss::RfcommCommandResponseAndDirection::COMMAND_FROM_INITIATOR);
561 rfcomm.channel().Write(params.rfcomm_channel);
562
563 rfcomm.control().Write(
564 emboss::RfcommFrameType::UNNUMBERED_INFORMATION_WITH_HEADER_CHECK);
565
566 rfcomm.length_extended_flag().Write(emboss::RfcommLengthExtended::NORMAL);
567 // Invalid length.
568 rfcomm.length().Write(1);
569 // Can't Write FCS as emboss will assert because of invalid length. Place
570 // manually.
571 pw::span<uint8_t> hci_span = bframe.acl.hci_span();
572 hci_span[hci_span.size() - 1] = kExpectedFcs;
573
574 H4PacketWithHci packet{emboss::H4PacketType::ACL_DATA, hci_span};
575 proxy.HandleH4HciFromController(std::move(packet));
576 }
577
578 EXPECT_EQ(capture.rx_called, 0);
579 EXPECT_EQ(capture.host_called, 2);
580 }
581
582 } // namespace
583 } // namespace pw::bluetooth::proxy
584