• 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_sapphire/internal/host/iso/iso_stream.h"
16 
17 #include "pw_bluetooth/hci_data.emb.h"
18 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
19 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
21 
22 namespace bt::iso {
23 namespace {
24 
25 using pw::bluetooth::emboss::IsoDataPacketStatus;
26 using pw::bluetooth::emboss::IsoDataPbFlag;
27 
28 constexpr hci_spec::CigIdentifier kCigId = 0x22;
29 constexpr hci_spec::CisIdentifier kCisId = 0x42;
30 
31 constexpr hci_spec::ConnectionHandle kCisHandleId = 0x59e;
32 
33 constexpr size_t kMaxControllerSDUFragmentSize = 100;
34 constexpr size_t kMaxControllerPacketCount = 9;
35 
36 constexpr size_t kSduHeaderSize = 4;
37 
38 using MockControllerTestBase =
39     bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
40 
41 class IsoStreamTest : public MockControllerTestBase {
42  public:
43   IsoStreamTest() = default;
44   ~IsoStreamTest() override = default;
45 
SetUp()46   void SetUp() override {
47     MockControllerTestBase::SetUp(
48         pw::bluetooth::Controller::FeaturesBits::kHciIso);
49     hci::DataBufferInfo iso_buffer_info(kMaxControllerSDUFragmentSize,
50                                         kMaxControllerPacketCount);
51     transport()->InitializeIsoDataChannel(iso_buffer_info);
52     iso_stream_ = IsoStream::Create(
53         kCigId,
54         kCisId,
55         kCisHandleId,
56         /*on_established_cb=*/
57         [this](pw::bluetooth::emboss::StatusCode status,
58                std::optional<WeakSelf<IsoStream>::WeakPtr>,
59                const std::optional<CisEstablishedParameters>& parameters) {
60           ASSERT_FALSE(establishment_status_.has_value());
61           establishment_status_ = status;
62           established_parameters_ = parameters;
63         },
64         transport()->command_channel()->AsWeakPtr(),
65         /*on_closed_cb=*/
66         [this]() {
67           ASSERT_FALSE(closed_);
68           closed_ = true;
69         },
70         transport()->iso_data_channel());
71   }
72 
73  protected:
74   // Send an HCI_LE_CIS_Established event with the provided status
75   void EstablishCis(pw::bluetooth::emboss::StatusCode status);
76 
77   // Call IsoStream::SetupDataPath().
78   // |cmd_complete_status| is nullopt if we do not expect an
79   // LE_Setup_ISO_Data_Path command to be generated, otherwise it should be set
80   // to the status code we want to generate in the response frame.
81   // |expected_cb_result| should be set to the expected result of the callback
82   // from IsoStream::SetupDataPath.
83   void SetupDataPath(pw::bluetooth::emboss::DataPathDirection direction,
84                      const std::optional<std::vector<uint8_t>>& codec_config,
85                      const std::optional<pw::bluetooth::emboss::StatusCode>&
86                          cmd_complete_status,
87                      iso::IsoStream::SetupDataPathError expected_cb_result,
88                      bool generate_mismatched_cid = false);
89 
RegisterStream()90   void RegisterStream() {
91     transport()->iso_data_channel()->RegisterConnection(
92         kCisHandleId, iso_stream()->GetWeakPtr());
93   }
94 
95   bool HandleCompleteIncomingSDU(const pw::span<const std::byte>& complete_sdu);
96 
iso_stream()97   IsoStream* iso_stream() { return iso_stream_.get(); }
98 
complete_incoming_sdus()99   std::queue<std::vector<std::byte>>* complete_incoming_sdus() {
100     return &complete_incoming_sdus_;
101   }
102 
establishment_status()103   std::optional<pw::bluetooth::emboss::StatusCode> establishment_status() {
104     return establishment_status_;
105   }
106 
established_parameters()107   std::optional<CisEstablishedParameters> established_parameters() {
108     return established_parameters_;
109   }
110 
closed()111   bool closed() { return closed_; }
112 
113  protected:
114   bool accept_incoming_sdus_ = true;
115 
116  private:
117   std::unique_ptr<IsoStream> iso_stream_;
118   std::optional<pw::bluetooth::emboss::StatusCode> establishment_status_;
119   std::optional<CisEstablishedParameters> established_parameters_;
120   std::queue<std::vector<std::byte>> complete_incoming_sdus_;
121   bool closed_ = false;
122 };
123 
LECisEstablishedPacketWithDefaultValues(pw::bluetooth::emboss::StatusCode status)124 static DynamicByteBuffer LECisEstablishedPacketWithDefaultValues(
125     pw::bluetooth::emboss::StatusCode status) {
126   return testing::LECisEstablishedEventPacket(
127       status,
128       kCisHandleId,
129       /*cig_sync_delay_us=*/0x123456,  // Must be in [0x0000ea, 0x7fffff]
130       /*cis_sync_delay_us=*/0x7890ab,  // Must be in [0x0000ea, 0x7fffff]
131       /*transport_latency_c_to_p_us=*/0x654321,  // Must be in [0x0000ea,
132                                                  // 0x7fffff]
133       /*transport_latency_p_to_c_us=*/0x0fedcb,  // Must be in [0x0000ea,
134                                                  // 0x7fffff]
135       /*phy_c_to_p=*/pw::bluetooth::emboss::IsoPhyType::LE_2M,
136       /*phy_c_to_p=*/pw::bluetooth::emboss::IsoPhyType::LE_CODED,
137       /*nse=*/0x10,               // Must be in [0x01, 0x1f]
138       /*bn_c_to_p=*/0x05,         // Must be in [0x00, 0x0f]
139       /*bn_p_to_c=*/0x0f,         // Must be in [0x00, 0x0f]
140       /*ft_c_to_p=*/0x01,         // Must be in [0x01, 0xff]
141       /*ft_p_to_c=*/0xff,         // Must be in [0x01, 0xff]
142       /*max_pdu_c_to_p=*/0x0042,  // Must be in [0x0000, 0x00fb]
143       /*max_pdu_p_to_c=*/0x00fb,  // Must be in [0x0000, 0x00fb]
144       /*iso_interval=*/0x0222     // Must be in [0x0004, 0x0c80]
145   );
146 }
147 
EstablishCis(pw::bluetooth::emboss::StatusCode status)148 void IsoStreamTest::EstablishCis(pw::bluetooth::emboss::StatusCode status) {
149   DynamicByteBuffer le_cis_established_packet =
150       LECisEstablishedPacketWithDefaultValues(status);
151   test_device()->SendCommandChannelPacket(le_cis_established_packet);
152   RunUntilIdle();
153   ASSERT_TRUE(establishment_status().has_value());
154   EXPECT_EQ(*(establishment_status()), status);
155   if (status == pw::bluetooth::emboss::StatusCode::SUCCESS) {
156     EXPECT_TRUE(established_parameters().has_value());
157   } else {
158     EXPECT_FALSE(established_parameters().has_value());
159   }
160 }
161 
GenerateCodecId()162 bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter> GenerateCodecId() {
163   const uint16_t kCompanyId = 0x1234;
164   bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter> codec_id;
165   auto codec_id_view = codec_id.view();
166   codec_id_view.coding_format().Write(pw::bluetooth::emboss::CodingFormat::LC3);
167   codec_id_view.company_id().Write(kCompanyId);
168   return codec_id;
169 }
170 
SetupDataPath(pw::bluetooth::emboss::DataPathDirection direction,const std::optional<std::vector<uint8_t>> & codec_configuration,const std::optional<pw::bluetooth::emboss::StatusCode> & cmd_complete_status,iso::IsoStream::SetupDataPathError expected_cb_result,bool generate_mismatched_cid)171 void IsoStreamTest::SetupDataPath(
172     pw::bluetooth::emboss::DataPathDirection direction,
173     const std::optional<std::vector<uint8_t>>& codec_configuration,
174     const std::optional<pw::bluetooth::emboss::StatusCode>& cmd_complete_status,
175     iso::IsoStream::SetupDataPathError expected_cb_result,
176     bool generate_mismatched_cid) {
177   const uint32_t kControllerDelay = 1234;  // Must be < 4000000
178   std::optional<iso::IsoStream::SetupDataPathError> actual_cb_result;
179 
180   if (cmd_complete_status.has_value()) {
181     auto setup_data_path_packet =
182         testing::LESetupIsoDataPathPacket(kCisHandleId,
183                                           direction,
184                                           /*HCI*/ 0,
185                                           GenerateCodecId(),
186                                           kControllerDelay,
187                                           codec_configuration);
188     hci_spec::ConnectionHandle cis_handle =
189         kCisHandleId + (generate_mismatched_cid ? 1 : 0);
190     auto setup_data_path_complete_packet =
191         testing::LESetupIsoDataPathResponse(*cmd_complete_status, cis_handle);
192     EXPECT_CMD_PACKET_OUT(test_device(),
193                           setup_data_path_packet,
194                           &setup_data_path_complete_packet);
195   }
196 
197   iso_stream()->SetupDataPath(
198       direction,
199       GenerateCodecId(),
200       codec_configuration,
201       kControllerDelay,
202       [&actual_cb_result](iso::IsoStream::SetupDataPathError result) {
203         actual_cb_result = result;
204       },
205       fit::bind_member<&IsoStreamTest::HandleCompleteIncomingSDU>(this));
206   RunUntilIdle();
207   ASSERT_TRUE(actual_cb_result.has_value());
208   EXPECT_EQ(expected_cb_result, *actual_cb_result);
209 }
210 
HandleCompleteIncomingSDU(const pw::span<const std::byte> & sdu)211 bool IsoStreamTest::HandleCompleteIncomingSDU(
212     const pw::span<const std::byte>& sdu) {
213   if (!accept_incoming_sdus_) {
214     return false;
215   }
216   std::vector<std::byte> new_sdu(sdu.size());
217   std::copy(sdu.begin(), sdu.end(), new_sdu.begin());
218   complete_incoming_sdus_.emplace(std::move(new_sdu));
219   return true;
220 }
221 
TEST_F(IsoStreamTest,CisEstablishedSuccessfully)222 TEST_F(IsoStreamTest, CisEstablishedSuccessfully) {
223   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
224 }
225 
TEST_F(IsoStreamTest,CisEstablishmentFailed)226 TEST_F(IsoStreamTest, CisEstablishmentFailed) {
227   EstablishCis(pw::bluetooth::emboss::StatusCode::MEMORY_CAPACITY_EXCEEDED);
228 }
229 
TEST_F(IsoStreamTest,ClosedCallsCloseCallback)230 TEST_F(IsoStreamTest, ClosedCallsCloseCallback) {
231   EXPECT_FALSE(closed());
232   iso_stream()->Close();
233   EXPECT_TRUE(closed());
234 }
235 
TEST_F(IsoStreamTest,SetupDataPathSuccessfully)236 TEST_F(IsoStreamTest, SetupDataPathSuccessfully) {
237   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
238   SetupDataPath(pw::bluetooth::emboss::DataPathDirection::OUTPUT,
239                 /*codec_configuration=*/std::nullopt,
240                 pw::bluetooth::emboss::StatusCode::SUCCESS,
241                 iso::IsoStream::SetupDataPathError::kSuccess);
242 }
243 
TEST_F(IsoStreamTest,SetupDataPathBeforeCisEstablished)244 TEST_F(IsoStreamTest, SetupDataPathBeforeCisEstablished) {
245   SetupDataPath(pw::bluetooth::emboss::DataPathDirection::OUTPUT,
246                 /*codec_configuration=*/std::nullopt,
247                 /*cmd_complete_status=*/std::nullopt,
248                 iso::IsoStream::SetupDataPathError::kCisNotEstablished);
249 }
250 
TEST_F(IsoStreamTest,SetupInputDataPathTwice)251 TEST_F(IsoStreamTest, SetupInputDataPathTwice) {
252   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
253   SetupDataPath(
254       pw::bluetooth::emboss::DataPathDirection::INPUT,
255       /*codec_configuration=*/std::nullopt,
256       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
257       iso::IsoStream::SetupDataPathError::kSuccess);
258   SetupDataPath(pw::bluetooth::emboss::DataPathDirection::INPUT,
259                 /*codec_configuration=*/std::nullopt,
260                 /*cmd_complete_status=*/std::nullopt,
261                 iso::IsoStream::SetupDataPathError::kStreamAlreadyExists);
262 }
263 
TEST_F(IsoStreamTest,SetupOutputDataPathTwice)264 TEST_F(IsoStreamTest, SetupOutputDataPathTwice) {
265   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
266   SetupDataPath(
267       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
268       /*codec_configuration=*/std::nullopt,
269       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
270       iso::IsoStream::SetupDataPathError::kSuccess);
271   SetupDataPath(pw::bluetooth::emboss::DataPathDirection::OUTPUT,
272                 /*codec_configuration=*/std::nullopt,
273                 /*cmd_complete_status=*/std::nullopt,
274                 iso::IsoStream::SetupDataPathError::kStreamAlreadyExists);
275 }
276 
TEST_F(IsoStreamTest,SetupBothInputAndOutputDataPaths)277 TEST_F(IsoStreamTest, SetupBothInputAndOutputDataPaths) {
278   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
279   SetupDataPath(
280       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
281       /*codec_configuration=*/std::nullopt,
282       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
283       iso::IsoStream::SetupDataPathError::kSuccess);
284   SetupDataPath(
285       pw::bluetooth::emboss::DataPathDirection::INPUT,
286       /*codec_configuration=*/std::nullopt,
287       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
288       iso::IsoStream::SetupDataPathError::kSuccess);
289 }
290 
TEST_F(IsoStreamTest,SetupDataPathInvalidArgs)291 TEST_F(IsoStreamTest, SetupDataPathInvalidArgs) {
292   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
293   SetupDataPath(static_cast<pw::bluetooth::emboss::DataPathDirection>(250),
294                 /*codec_configuration=*/std::nullopt,
295                 /*cmd_complete_status=*/std::nullopt,
296                 iso::IsoStream::SetupDataPathError::kInvalidArgs);
297 }
298 
TEST_F(IsoStreamTest,SetupDataPathWithCodecConfig)299 TEST_F(IsoStreamTest, SetupDataPathWithCodecConfig) {
300   std::vector<uint8_t> codec_config{5, 6, 7, 8};
301   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
302   SetupDataPath(
303       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
304       codec_config,
305       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
306       iso::IsoStream::SetupDataPathError::kSuccess);
307 }
308 
309 // If the connection ID doesn't match in the command complete packet, fail
TEST_F(IsoStreamTest,SetupDataPathHandleMismatch)310 TEST_F(IsoStreamTest, SetupDataPathHandleMismatch) {
311   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
312   SetupDataPath(
313       pw::bluetooth::emboss::DataPathDirection::INPUT,
314       /*codec_configuration=*/std::nullopt,
315       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
316       iso::IsoStream::SetupDataPathError::kStreamRejectedByController,
317       /*generate_mismatched_cid=*/true);
318 }
319 
TEST_F(IsoStreamTest,SetupDataPathControllerError)320 TEST_F(IsoStreamTest, SetupDataPathControllerError) {
321   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
322   SetupDataPath(
323       pw::bluetooth::emboss::DataPathDirection::INPUT,
324       /*codec_configuration=*/std::nullopt,
325       /*cmd_complete_status=*/
326       pw::bluetooth::emboss::StatusCode::CONNECTION_ALREADY_EXISTS,
327       iso::IsoStream::SetupDataPathError::kStreamRejectedByController);
328 }
329 
330 // If the client asks for frames before any are ready it will receive
331 // a notification when the next packet arrives.
TEST_F(IsoStreamTest,PendingRead)332 TEST_F(IsoStreamTest, PendingRead) {
333   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
334   SetupDataPath(
335       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
336       /*codec_configuration=*/std::nullopt,
337       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
338       iso::IsoStream::SetupDataPathError::kSuccess);
339   const size_t kIsoSduLength = 212;
340   std::unique_ptr<std::vector<uint8_t>> sdu_data =
341       testing::GenDataBlob(kIsoSduLength, /*starting_value=*/14);
342   DynamicByteBuffer packet0 = testing::IsoDataPacket(
343       /*connection_handle=*/iso_stream()->cis_handle(),
344       pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
345       /*time_stamp=*/std::nullopt,
346       /*packet_sequence_number=*/0,
347       kIsoSduLength,
348       pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA,
349       *sdu_data);
350   pw::span<const std::byte> packet0_as_span = packet0.subspan();
351   ASSERT_EQ(iso_stream()->ReadNextQueuedIncomingPacket(), nullptr);
352   iso_stream()->ReceiveInboundPacket(packet0_as_span);
353   ASSERT_EQ(complete_incoming_sdus()->size(), 1u);
354   std::vector<std::byte>& received_frame = complete_incoming_sdus()->front();
355   ASSERT_EQ(packet0_as_span.size(), received_frame.size());
356   EXPECT_TRUE(std::equal(
357       packet0_as_span.begin(), packet0_as_span.end(), received_frame.begin()));
358 }
359 
360 // If the client does not ask for frames it will not receive any notifications
361 // and the IsoStream will just queue them up.
TEST_F(IsoStreamTest,UnreadData)362 TEST_F(IsoStreamTest, UnreadData) {
363   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
364   SetupDataPath(
365       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
366       /*codec_configuration=*/std::nullopt,
367       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
368       iso::IsoStream::SetupDataPathError::kSuccess);
369   const size_t kTotalFrameCount = 5;
370   DynamicByteBuffer packets[kTotalFrameCount];
371   pw::span<const std::byte> packets_as_span[kTotalFrameCount];
372   for (size_t i = 0; i < kTotalFrameCount; i++) {
373     std::unique_ptr<std::vector<uint8_t>> sdu_data = testing::GenDataBlob(
374         /*size=*/kMaxControllerSDUFragmentSize - i, /*starting_value=*/i);
375     packets[i] = testing::IsoDataPacket(
376         /*connection_handle=*/iso_stream()->cis_handle(),
377         pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
378         /*time_stamp=*/std::nullopt,
379         /*packet_sequence_number=*/i,
380         /*iso_sdu_length=*/kMaxControllerSDUFragmentSize - i,
381         pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA,
382         *sdu_data);
383     packets_as_span[i] = packets[i].subspan();
384     iso_stream()->ReceiveInboundPacket(packets_as_span[i]);
385   }
386   EXPECT_EQ(complete_incoming_sdus()->size(), 0u);
387 }
388 
389 // This is the (somewhat unusual) case where the client asks for a frame but
390 // then rejects it when the frame is ready. The frame should stay in the queue
391 // and future frames should not receive notification, either.
TEST_F(IsoStreamTest,ReadRequestedAndThenRejected)392 TEST_F(IsoStreamTest, ReadRequestedAndThenRejected) {
393   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
394   SetupDataPath(
395       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
396       /*codec_configuration=*/std::nullopt,
397       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
398       iso::IsoStream::SetupDataPathError::kSuccess);
399   size_t packet0_sdu_fragment_size = kMaxControllerSDUFragmentSize;
400   std::unique_ptr<std::vector<uint8_t>> packet0_sdu_data =
401       testing::GenDataBlob(packet0_sdu_fragment_size, /*starting_value=*/11);
402   DynamicByteBuffer packet0 = testing::IsoDataPacket(
403       /*connection_handle=*/iso_stream()->cis_handle(),
404       pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
405       /*time_stamp=*/std::nullopt,
406       /*packet_sequence_number=*/0,
407       /*iso_sdu_length=*/packet0_sdu_fragment_size,
408       pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA,
409       *packet0_sdu_data);
410   pw::span<const std::byte> packet0_as_span = packet0.subspan();
411   size_t packet1_sdu_fragment_size = packet0_sdu_fragment_size - 1;
412   std::unique_ptr<std::vector<uint8_t>> packet1_sdu_data =
413       testing::GenDataBlob(packet1_sdu_fragment_size, /*starting_value=*/13);
414   DynamicByteBuffer packet1 = testing::IsoDataPacket(
415       /*connection_handle=*/iso_stream()->cis_handle(),
416       pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
417       /*time_stamp=*/std::nullopt,
418       /*packet_sequence_number=*/1,
419       /*iso_sdu_length=*/packet1_sdu_fragment_size,
420       pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA,
421       *packet1_sdu_data);
422   pw::span<const std::byte> packet1_as_span = packet1.subspan();
423 
424   // Request a frame but then reject it when proffered by the stream
425   ASSERT_EQ(iso_stream()->ReadNextQueuedIncomingPacket(), nullptr);
426   accept_incoming_sdus_ = false;
427   iso_stream()->ReceiveInboundPacket(packet0_as_span);
428   EXPECT_EQ(complete_incoming_sdus()->size(), 0u);
429 
430   // Accept future frames, but because no read request has been made that we
431   // couldn't fulfill, the stream should just queue them up.
432   accept_incoming_sdus_ = true;
433   iso_stream()->ReceiveInboundPacket(packet1_as_span);
434   EXPECT_EQ(complete_incoming_sdus()->size(), 0u);
435 
436   // And finally, we should be able to read out the packets in the right order
437   std::unique_ptr<IsoDataPacket> rx_packet_0 =
438       iso_stream()->ReadNextQueuedIncomingPacket();
439   ASSERT_NE(rx_packet_0, nullptr);
440   ASSERT_EQ(rx_packet_0->size(), packet0_as_span.size());
441   ASSERT_TRUE(std::equal(
442       rx_packet_0->begin(), rx_packet_0->end(), packet0_as_span.begin()));
443   std::unique_ptr<IsoDataPacket> rx_packet_1 =
444       iso_stream()->ReadNextQueuedIncomingPacket();
445   ASSERT_NE(rx_packet_1, nullptr);
446   ASSERT_EQ(rx_packet_1->size(), packet1_as_span.size());
447   ASSERT_TRUE(std::equal(
448       rx_packet_1->begin(), rx_packet_1->end(), packet1_as_span.begin()));
449 
450   // Stream's packet queue should be empty now
451   ASSERT_EQ(iso_stream()->ReadNextQueuedIncomingPacket(), nullptr);
452 }
453 
454 // Bad packets will not be passed on
TEST_F(IsoStreamTest,BadPacket)455 TEST_F(IsoStreamTest, BadPacket) {
456   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
457   SetupDataPath(
458       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
459       /*codec_configuration=*/std::nullopt,
460       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
461       iso::IsoStream::SetupDataPathError::kSuccess);
462   const size_t kIsoSduLength = 212;
463   std::unique_ptr<std::vector<uint8_t>> sdu_data =
464       testing::GenDataBlob(kIsoSduLength, /*starting_value=*/91);
465   DynamicByteBuffer packet0 = testing::IsoDataPacket(
466       /*connection_handle=*/iso_stream()->cis_handle(),
467       pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
468       /*time_stamp=*/std::nullopt,
469       /*packet_sequence_number=*/0,
470       kIsoSduLength,
471       pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA,
472       *sdu_data);
473   // Improperly formatted packets are discarded. To test this, we'll remove the
474   // last byte of SDU data before we send it.
475   pw::span<const std::byte> packet0_as_span =
476       packet0.subspan(0, packet0.size() - 1);
477   ASSERT_EQ(iso_stream()->ReadNextQueuedIncomingPacket(), nullptr);
478   iso_stream()->ReceiveInboundPacket(packet0_as_span);
479   ASSERT_EQ(complete_incoming_sdus()->size(), 0u);
480 }
481 
482 // Extra data at the end of the frame will be removed
TEST_F(IsoStreamTest,ExcessDataIsTruncated)483 TEST_F(IsoStreamTest, ExcessDataIsTruncated) {
484   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
485   SetupDataPath(
486       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
487       /*codec_configuration=*/std::nullopt,
488       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
489       iso::IsoStream::SetupDataPathError::kSuccess);
490   const size_t kIsoSduLength = 212;
491   std::unique_ptr<std::vector<uint8_t>> sdu_data =
492       testing::GenDataBlob(kIsoSduLength, /*starting_value=*/91);
493   DynamicByteBuffer packet0 = testing::IsoDataPacket(
494       /*connection_handle=*/iso_stream()->cis_handle(),
495       pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
496       /*time_stamp=*/std::nullopt,
497       /*packet_sequence_number=*/0,
498       kIsoSduLength,
499       pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA,
500       *sdu_data);
501   size_t original_packet0_size = packet0.size();
502   packet0.expand(packet0.size() + 100);
503   pw::span<const std::byte> packet0_as_span = packet0.subspan();
504   ASSERT_EQ(iso_stream()->ReadNextQueuedIncomingPacket(), nullptr);
505   iso_stream()->ReceiveInboundPacket(packet0_as_span);
506   ASSERT_EQ(complete_incoming_sdus()->size(), 1u);
507   EXPECT_EQ(complete_incoming_sdus()->front().size(), original_packet0_size);
508 }
509 
510 // Ensure sent packets are structured and sent correctly, including
511 // fragmentation.
TEST_F(IsoStreamTest,SendPacket)512 TEST_F(IsoStreamTest, SendPacket) {
513   EstablishCis(pw::bluetooth::emboss::StatusCode::SUCCESS);
514   SetupDataPath(
515       pw::bluetooth::emboss::DataPathDirection::OUTPUT,
516       /*codec_configuration=*/std::nullopt,
517       /*cmd_complete_status=*/pw::bluetooth::emboss::StatusCode::SUCCESS,
518       iso::IsoStream::SetupDataPathError::kSuccess);
519   RegisterStream();
520 
521   constexpr size_t kMaxFirstPacketSize =
522       kMaxControllerSDUFragmentSize - kSduHeaderSize;
523 
524   {
525     std::vector<uint8_t> blob =
526         *testing::GenDataBlob(kMaxFirstPacketSize / 2, /*starting_value=*/111);
527     EXPECT_ISO_PACKET_OUT(
528         test_device(),
529         testing::IsoDataPacket(
530             /*connection_handle = */ iso_stream()->cis_handle(),
531             /*pb_flag = */ IsoDataPbFlag::COMPLETE_SDU,
532             /*timestamp = */ std::nullopt,
533             /*sequence_number = */ 0,
534             /*sdu_length = */ blob.size(),
535             /*status_flag = */ IsoDataPacketStatus::VALID_DATA,
536             /*sdu_data = */ pw::span(blob.data(), blob.size())));
537 
538     iso_stream()->Send(
539         pw::span(reinterpret_cast<const std::byte*>(blob.data()), blob.size()));
540     RunUntilIdle();
541     EXPECT_TRUE(test_device()->AllExpectedIsoPacketsSent());
542   }
543 
544   {
545     // Send a packet that fits exactly within one ISO buffer easily.
546     std::vector<uint8_t> blob =
547         *testing::GenDataBlob(kMaxFirstPacketSize, /*starting_value=*/7);
548     EXPECT_ISO_PACKET_OUT(
549         test_device(),
550         testing::IsoDataPacket(
551             /*connection_handle = */ iso_stream()->cis_handle(),
552             /*pb_flag = */ IsoDataPbFlag::COMPLETE_SDU,
553             /*timestamp = */ std::nullopt,
554             /*sequence_number = */ 0,
555             /*sdu_length = */ blob.size(),
556             /*status_flag = */ IsoDataPacketStatus::VALID_DATA,
557             /*sdu_data = */ pw::span(blob.data(), blob.size())));
558 
559     iso_stream()->Send(
560         pw::span(reinterpret_cast<const std::byte*>(blob.data()), blob.size()));
561     RunUntilIdle();
562     EXPECT_TRUE(test_device()->AllExpectedIsoPacketsSent());
563   }
564 
565   {
566     // Send a packet that has to be split into three.
567     std::vector<uint8_t> blob = *testing::GenDataBlob(
568         kMaxFirstPacketSize + kMaxControllerSDUFragmentSize * 2,
569         /*starting_value=*/42);
570     EXPECT_ISO_PACKET_OUT(
571         test_device(),
572         testing::IsoDataPacket(
573             /*connection_handle = */ iso_stream()->cis_handle(),
574             /*pb_flag = */ IsoDataPbFlag::FIRST_FRAGMENT,
575             /*timestamp = */ std::nullopt,
576             /*sequence_number = */ 0,
577             /*sdu_length = */ blob.size(),
578             /*status_flag = */ IsoDataPacketStatus::VALID_DATA,
579             /*sdu_data = */ pw::span(blob.data(), kMaxFirstPacketSize)));
580     EXPECT_ISO_PACKET_OUT(
581         test_device(),
582         testing::IsoDataPacket(
583             /*connection_handle = */ iso_stream()->cis_handle(),
584             /*pb_flag = */ IsoDataPbFlag::INTERMEDIATE_FRAGMENT,
585             /*timestamp = */ std::nullopt,
586             /*sequence_number = */ std::nullopt,
587             /*sdu_length = */ std::nullopt,
588             /*status_flag = */ std::nullopt,
589             /*sdu_data = */
590             pw::span(blob.data(), blob.size())
591                 .subspan(kMaxFirstPacketSize, kMaxControllerSDUFragmentSize)));
592     EXPECT_ISO_PACKET_OUT(
593         test_device(),
594         testing::IsoDataPacket(
595             /*connection_handle = */ iso_stream()->cis_handle(),
596             /*pb_flag = */ IsoDataPbFlag::LAST_FRAGMENT,
597             /*timestamp = */ std::nullopt,
598             /*sequence_number = */ std::nullopt,
599             /*sdu_length = */ std::nullopt,
600             /*status_flag = */ std::nullopt,
601             /*sdu_data = */
602             pw::span(blob.data(), blob.size())
603                 .subspan(kMaxFirstPacketSize + kMaxControllerSDUFragmentSize)));
604 
605     iso_stream()->Send(
606         pw::span(reinterpret_cast<const std::byte*>(blob.data()), blob.size()));
607     RunUntilIdle();
608     EXPECT_TRUE(test_device()->AllExpectedIsoPacketsSent());
609   }
610 
611   {
612     // Send a packet that just barely needs to be split into three fragments.
613     std::vector<uint8_t> blob = *testing::GenDataBlob(
614         kMaxFirstPacketSize + kMaxControllerSDUFragmentSize + 1,
615         /*starting_value=*/77);
616     EXPECT_ISO_PACKET_OUT(
617         test_device(),
618         testing::IsoDataPacket(
619             /*connection_handle = */ iso_stream()->cis_handle(),
620             /*pb_flag = */ IsoDataPbFlag::FIRST_FRAGMENT,
621             /*timestamp = */ std::nullopt,
622             /*sequence_number = */ 0,
623             /*sdu_length = */ blob.size(),
624             /*status_flag = */ IsoDataPacketStatus::VALID_DATA,
625             /*sdu_data = */ pw::span(blob.data(), kMaxFirstPacketSize)));
626     EXPECT_ISO_PACKET_OUT(
627         test_device(),
628         testing::IsoDataPacket(
629             /*connection_handle = */ iso_stream()->cis_handle(),
630             /*pb_flag = */ IsoDataPbFlag::INTERMEDIATE_FRAGMENT,
631             /*timestamp = */ std::nullopt,
632             /*sequence_number = */ std::nullopt,
633             /*sdu_length = */ std::nullopt,
634             /*status_flag = */ std::nullopt,
635             /*sdu_data = */
636             pw::span(blob.data(), blob.size())
637                 .subspan(kMaxFirstPacketSize, kMaxControllerSDUFragmentSize)));
638     EXPECT_ISO_PACKET_OUT(
639         test_device(),
640         testing::IsoDataPacket(
641             /*connection_handle = */ iso_stream()->cis_handle(),
642             /*pb_flag = */ IsoDataPbFlag::LAST_FRAGMENT,
643             /*timestamp = */ std::nullopt,
644             /*sequence_number = */ std::nullopt,
645             /*sdu_length = */ std::nullopt,
646             /*status_flag = */ std::nullopt,
647             /*sdu_data = */
648             pw::span(blob.data(), blob.size())
649                 .subspan(kMaxFirstPacketSize + kMaxControllerSDUFragmentSize)));
650 
651     iso_stream()->Send(
652         pw::span(reinterpret_cast<const std::byte*>(blob.data()), blob.size()));
653     RunUntilIdle();
654     EXPECT_TRUE(test_device()->AllExpectedIsoPacketsSent());
655   }
656 }
657 
658 }  // namespace
659 }  // namespace bt::iso
660