• 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_assert/check.h>
18 
19 #include "pw_bluetooth/hci_data.emb.h"
20 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
21 #include "pw_bluetooth_sapphire/internal/host/hci/sequential_command_runner.h"
22 #include "pw_bluetooth_sapphire/internal/host/iso/iso_inbound_packet_assembler.h"
23 #include "pw_bytes/span.h"
24 
25 namespace bt::iso {
26 
27 // These values are unfortunately not available for extracting from the emboss
28 // definition directly.
29 constexpr size_t kTimestampSize = 4;
30 constexpr size_t kSduHeaderSize = 4;
31 constexpr size_t kFrameHeaderSize =
32     pw::bluetooth::emboss::IsoDataFrameHeaderView::SizeInBytes();
33 
34 // The next few functions are helpers for determining the size of packets and
35 // the buffer space required to send them.
36 //
37 // BT Core spec v5.4, Vol 4, Part E
38 //
39 // Sec 4.1.1
40 //   The ISO_Data_Packet_Length parameter [...] specifies the maximum buffer
41 //   size for each HCI ISO Data packet (excluding the header but including
42 //   optional fields such as ISO_SDU_Length).
43 //
44 // Sec 5.4.5
45 //   In the Host to Controller direction, Data_Total_Length shall be less than
46 //   or equal to the size of the buffer supported by the Controller (which is
47 //   returned using the ISO_Data_Packet_Length return parameter [...].
OptionalFieldLength(bool has_timestamp,bool has_sdu_header)48 constexpr size_t OptionalFieldLength(bool has_timestamp, bool has_sdu_header) {
49   return (has_timestamp ? kTimestampSize : 0) +
50          (has_sdu_header ? kSduHeaderSize : 0);
51 }
TotalDataLength(bool has_timestamp,bool has_sdu_header,size_t data_size)52 constexpr size_t TotalDataLength(bool has_timestamp,
53                                  bool has_sdu_header,
54                                  size_t data_size) {
55   // The total data length is the size of the payload plus optional fields.
56   return OptionalFieldLength(has_timestamp, has_sdu_header) + data_size;
57 }
TotalPacketSize(bool has_timestamp,bool has_sdu_header,size_t data_size)58 constexpr size_t TotalPacketSize(bool has_timestamp,
59                                  bool has_sdu_header,
60                                  size_t data_size) {
61   // The entire packet also contains a fixed size header, this is not included
62   // when calculating the size/maximum size for the controller buffers.
63   return kFrameHeaderSize +
64          TotalDataLength(has_timestamp, has_sdu_header, data_size);
65 }
FragmentDataLength(bool has_timestamp,bool has_sdu_header,size_t data_size)66 constexpr size_t FragmentDataLength(bool has_timestamp,
67                                     bool has_sdu_header,
68                                     size_t data_size) {
69   // The length of the actual SDU data contained in the fragment.
70   return data_size - OptionalFieldLength(has_timestamp, has_sdu_header);
71 }
72 
73 // Return two subspans of the provided span, the first containing elements
74 // indexed by the interval [0, at), the second containing the elements indexed
75 // by the interval [at, size()).
76 template <typename T>
SplitSpan(pw::span<T> span,size_t at)77 std::pair<pw::span<T>, pw::span<T>> SplitSpan(pw::span<T> span, size_t at) {
78   if (at > span.size()) {
79     at = span.size();
80   }
81 
82   return {span.subspan(0, at), span.subspan(at)};
83 }
84 
85 class IsoStreamImpl final : public IsoStream {
86  public:
87   IsoStreamImpl(uint8_t cig_id,
88                 uint8_t cis_id,
89                 hci_spec::ConnectionHandle cis_handle,
90                 CisEstablishedCallback on_established_cb,
91                 hci::CommandChannel::WeakPtr cmd,
92                 pw::Callback<void()> on_closed_cb,
93                 hci::IsoDataChannel* data_channel);
94 
95   // IsoStream overrides
96   bool OnCisEstablished(const hci::EventPacket& event) override;
97   void SetupDataPath(
98       pw::bluetooth::emboss::DataPathDirection direction,
99       const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>& codec_id,
100       const std::optional<std::vector<uint8_t>>& codec_configuration,
101       uint32_t controller_delay_usecs,
102       SetupDataPathCallback&& on_complete_cb,
103       IncomingDataHandler&& on_incoming_data_available_cb) override;
cis_handle() const104   hci_spec::ConnectionHandle cis_handle() const override {
105     return cis_hci_handle_;
106   }
107   void Close() override;
108   std::unique_ptr<IsoDataPacket> ReadNextQueuedIncomingPacket() override;
109   void Send(pw::ConstByteSpan data) override;
GetWeakPtr()110   IsoStream::WeakPtr GetWeakPtr() override { return weak_self_.GetWeakPtr(); }
111 
112   // IsoDataChannel::ConnectionInterface override
113   void ReceiveInboundPacket(pw::span<const std::byte> packet) override;
114 
115  private:
116   struct SduHeaderInfo {
117     uint16_t packet_sequence_number;
118     uint16_t iso_sdu_length;
119   };
120 
121   void HandleCompletePacket(const pw::span<const std::byte>& packet);
122   DynamicByteBuffer BuildPacketForSending(
123       const pw::span<const std::byte>& data,
124       pw::bluetooth::emboss::IsoDataPbFlag pb_flag,
125       std::optional<SduHeaderInfo> sdu_header = std::nullopt,
126       std::optional<uint32_t> time_stamp = std::nullopt);
127 
128   enum class IsoStreamState {
129     kNotEstablished,
130     kEstablished,
131   } state_;
132 
133   uint8_t cig_id_ __attribute__((unused));
134   uint8_t cis_id_ __attribute__((unused));
135 
136   // Connection parameters, only valid after CIS is established
137   CisEstablishedParameters cis_params_;
138 
139   // Handle assigned by the controller
140   hci_spec::ConnectionHandle cis_hci_handle_;
141 
142   // Called after HCI_LE_CIS_Established event is received and handled
143   CisEstablishedCallback cis_established_cb_;
144 
145   IsoInboundPacketAssembler inbound_assembler_;
146 
147   IncomingDataHandler on_incoming_data_available_cb_;
148 
149   // When true, we will send a notification to the client when the next packet
150   // arrives. Otherwise, we will just queue it up.
151   bool inbound_client_is_waiting_ = false;
152 
153   std::queue<std::unique_ptr<std::vector<std::byte>>> incoming_data_queue_;
154 
155   // Called when stream is closed
156   pw::Callback<void()> on_closed_cb_;
157 
158   // Has the data path been configured?
159   enum class DataPathState {
160     kNotSetUp,
161     kSettingUp,
162     kSetUp,
163   };
164   DataPathState input_data_path_state_ = DataPathState::kNotSetUp;
165   DataPathState output_data_path_state_ = DataPathState::kNotSetUp;
166 
167   hci::CommandChannel::WeakPtr cmd_;
168 
169   hci::CommandChannel::EventHandlerId cis_established_handler_;
170 
171   // The IsoDataChannel that this stream is registered to.
172   hci::IsoDataChannel* data_channel_;
173 
174   WeakSelf<IsoStreamImpl> weak_self_;
175 
176   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(IsoStreamImpl);
177 };
178 
IsoStreamImpl(uint8_t cig_id,uint8_t cis_id,hci_spec::ConnectionHandle cis_handle,CisEstablishedCallback on_established_cb,hci::CommandChannel::WeakPtr cmd,pw::Callback<void ()> on_closed_cb,hci::IsoDataChannel * data_channel)179 IsoStreamImpl::IsoStreamImpl(uint8_t cig_id,
180                              uint8_t cis_id,
181                              hci_spec::ConnectionHandle cis_handle,
182                              CisEstablishedCallback on_established_cb,
183                              hci::CommandChannel::WeakPtr cmd,
184                              pw::Callback<void()> on_closed_cb,
185                              hci::IsoDataChannel* data_channel)
186     : IsoStream(),
187       state_(IsoStreamState::kNotEstablished),
188       cig_id_(cig_id),
189       cis_id_(cis_id),
190       cis_hci_handle_(cis_handle),
191       cis_established_cb_(std::move(on_established_cb)),
192       inbound_assembler_(
193           fit::bind_member<&IsoStreamImpl::HandleCompletePacket>(this)),
194       on_closed_cb_(std::move(on_closed_cb)),
195       cmd_(std::move(cmd)),
196       data_channel_(data_channel),
197       weak_self_(this) {
198   PW_CHECK(cmd_.is_alive());
199   PW_CHECK(data_channel_);
200 
201   auto weak_self = weak_self_.GetWeakPtr();
202   cis_established_handler_ = cmd_->AddLEMetaEventHandler(
203       hci_spec::kLECISEstablishedSubeventCode,
204       [self = std::move(weak_self)](const hci::EventPacket& event) {
205         if (!self.is_alive()) {
206           return hci::CommandChannel::EventCallbackResult::kRemove;
207         }
208         if (self->OnCisEstablished(event)) {
209           self->cis_established_handler_ = 0u;
210           return hci::CommandChannel::EventCallbackResult::kRemove;
211         }
212         return hci::CommandChannel::EventCallbackResult::kContinue;
213       });
214   PW_CHECK(cis_established_handler_ != 0u);
215 }
216 
OnCisEstablished(const hci::EventPacket & event)217 bool IsoStreamImpl::OnCisEstablished(const hci::EventPacket& event) {
218   PW_CHECK(event.event_code() == hci_spec::kLEMetaEventCode);
219   PW_CHECK(event.view<pw::bluetooth::emboss::LEMetaEventView>()
220                .subevent_code()
221                .Read() == hci_spec::kLECISEstablishedSubeventCode);
222   auto view = event.view<pw::bluetooth::emboss::LECISEstablishedSubeventView>();
223 
224   // Ignore any events intended for another CIS
225   hci_spec::ConnectionHandle handle = view.connection_handle().Read();
226   if (handle != cis_hci_handle_) {
227     bt_log(
228         INFO,
229         "iso",
230         "Ignoring CIS established notification for handle 0x%x (target: 0x%x)",
231         handle,
232         cis_hci_handle_);
233 
234     // Event not handled
235     return false;
236   }
237 
238   pw::bluetooth::emboss::StatusCode status = view.status().Read();
239   bt_log(INFO,
240          "iso",
241          "Handling CIS established notification for handle 0x%x (status: %s)",
242          handle,
243          hci_spec::StatusCodeToString(status).c_str());
244 
245   if (status != pw::bluetooth::emboss::StatusCode::SUCCESS) {
246     cis_established_cb_(status, std::nullopt, std::nullopt);
247     Close();
248     return true;
249   }
250 
251   state_ = IsoStreamState::kEstablished;
252 
253   // General stream attributes
254   cis_params_.cig_sync_delay = view.cig_sync_delay().Read();
255   cis_params_.cis_sync_delay = view.cis_sync_delay().Read();
256   cis_params_.max_subevents = view.nse().Read();
257   cis_params_.iso_interval = view.iso_interval().Read();
258 
259   // Central => Peripheral stream attributes
260   CisEstablishedParameters::CisUnidirectionalParams* params =
261       &cis_params_.c_to_p_params;
262   params->transport_latency = view.transport_latency_c_to_p().Read();
263   params->phy = view.phy_c_to_p().Read();
264   params->burst_number = view.bn_c_to_p().Read();
265   params->flush_timeout = view.ft_c_to_p().Read();
266   params->max_pdu_size = view.max_pdu_c_to_p().Read();
267 
268   // Peripheral => Central stream attributes
269   params = &cis_params_.p_to_c_params;
270   params->transport_latency = view.transport_latency_p_to_c().Read();
271   params->phy = view.phy_p_to_c().Read();
272   params->burst_number = view.bn_p_to_c().Read();
273   params->flush_timeout = view.ft_p_to_c().Read();
274   params->max_pdu_size = view.max_pdu_p_to_c().Read();
275 
276   cis_established_cb_(status, GetWeakPtr(), cis_params_);
277 
278   // Event handled
279   return true;
280 }
281 
SetupDataPath(pw::bluetooth::emboss::DataPathDirection direction,const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter> & codec_id,const std::optional<std::vector<uint8_t>> & codec_configuration,uint32_t controller_delay_usecs,SetupDataPathCallback && on_complete_cb,IncomingDataHandler && on_incoming_data_available_cb)282 void IsoStreamImpl::SetupDataPath(
283     pw::bluetooth::emboss::DataPathDirection direction,
284     const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>& codec_id,
285     const std::optional<std::vector<uint8_t>>& codec_configuration,
286     uint32_t controller_delay_usecs,
287     SetupDataPathCallback&& on_complete_cb,
288     IncomingDataHandler&& on_incoming_data_available_cb) {
289   if (state_ != IsoStreamState::kEstablished) {
290     bt_log(WARN, "iso", "failed to setup data path - CIS not established");
291     on_complete_cb(kCisNotEstablished);
292     return;
293   }
294 
295   DataPathState* target_data_path_state;
296   const char* direction_as_str;
297   switch (direction) {
298     case pw::bluetooth::emboss::DataPathDirection::INPUT:
299       target_data_path_state = &input_data_path_state_;
300       direction_as_str = "Input";
301       break;
302     case pw::bluetooth::emboss::DataPathDirection::OUTPUT:
303       target_data_path_state = &output_data_path_state_;
304       direction_as_str = "Output";
305       break;
306     default:
307       bt_log(WARN,
308              "iso",
309              "invalid data path direction (%u)",
310              static_cast<unsigned>(direction));
311       on_complete_cb(kInvalidArgs);
312       return;
313   }
314 
315   if (*target_data_path_state != DataPathState::kNotSetUp) {
316     bt_log(WARN,
317            "iso",
318            "attempt to setup %s CIS path - already setup",
319            direction_as_str);
320     on_complete_cb(kStreamAlreadyExists);
321     return;
322   }
323 
324   bt_log(INFO, "iso", "setting up CIS data path for %s", direction_as_str);
325   size_t packet_size =
326       pw::bluetooth::emboss::LESetupISODataPathCommand::MinSizeInBytes() +
327       (codec_configuration.has_value() ? codec_configuration->size() : 0);
328   auto cmd_packet = hci::CommandPacket::New<
329       pw::bluetooth::emboss::LESetupISODataPathCommandWriter>(
330       hci_spec::kLESetupISODataPath, packet_size);
331   auto cmd_view = cmd_packet.view_t();
332   cmd_view.connection_handle().Write(cis_hci_handle_);
333   cmd_view.data_path_direction().Write(direction);
334   cmd_view.data_path_id().Write(0);
335   cmd_view.codec_id().CopyFrom(
336       const_cast<bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>&>(
337           codec_id)
338           .view());
339   cmd_view.controller_delay().Write(controller_delay_usecs);
340   if (codec_configuration.has_value()) {
341     cmd_view.codec_configuration_length().Write(codec_configuration->size());
342     std::memcpy(cmd_view.codec_configuration().BackingStorage().data(),
343                 codec_configuration->data(),
344                 codec_configuration->size());
345   } else {
346     cmd_view.codec_configuration_length().Write(0);
347   }
348 
349   *target_data_path_state = DataPathState::kSettingUp;
350   WeakSelf<IsoStreamImpl>::WeakPtr self = weak_self_.GetWeakPtr();
351 
352   bt_log(INFO, "iso", "sending LE_Setup_ISO_Data_Path command");
353   cmd_->SendCommand(
354       std::move(cmd_packet),
355       [on_complete_callback = std::move(on_complete_cb),
356        self,
357        cis_handle = cis_hci_handle_,
358        target_data_path_state,
359        direction,
360        on_incoming_data_available_callback =
361            std::move(on_incoming_data_available_cb)](
362           auto, const hci::EventPacket& cmd_complete) mutable {
363         if (!self.is_alive()) {
364           on_complete_callback(kStreamClosed);
365           return;
366         }
367 
368         auto return_params =
369             cmd_complete.view<pw::bluetooth::emboss::
370                                   LESetupISODataPathCommandCompleteEventView>();
371         pw::bluetooth::emboss::StatusCode status =
372             return_params.status().Read();
373         hci_spec::ConnectionHandle connection_handle =
374             return_params.connection_handle().Read();
375 
376         if (status != pw::bluetooth::emboss::StatusCode::SUCCESS) {
377           bt_log(ERROR,
378                  "iso",
379                  "failed to setup ISO data path for handle 0x%x (status: 0x%x)",
380                  connection_handle,
381                  static_cast<uint8_t>(status));
382           *target_data_path_state = DataPathState::kNotSetUp;
383           on_complete_callback(kStreamRejectedByController);
384           return;
385         }
386 
387         // It's hard to know what is the right thing to do here. The controller
388         // accepted our request, but we don't agree on the connection handle ID.
389         // Something is amiss, so we will refuse to consider the data path
390         // setup even though the controller may think otherwise.
391         if (connection_handle != cis_handle) {
392           bt_log(ERROR,
393                  "iso",
394                  "handle mismatch in ISO data path setup completion (expected: "
395                  "0x%x, actual: %x)",
396                  cis_handle,
397                  connection_handle);
398           *target_data_path_state = DataPathState::kNotSetUp;
399           on_complete_callback(kStreamRejectedByController);
400           return;
401         }
402 
403         // Note that |direction| is a spec-defined value of dataflow direction
404         // relative to the controller, so this may look backwards.
405         if (direction == pw::bluetooth::emboss::DataPathDirection::OUTPUT) {
406           self->on_incoming_data_available_cb_ =
407               std::move(on_incoming_data_available_callback);
408         }
409         *target_data_path_state = DataPathState::kSetUp;
410         bt_log(INFO, "iso", "successfully set up data path");
411         on_complete_callback(kSuccess);
412       });
413 }
414 
ReceiveInboundPacket(pw::span<const std::byte> packet)415 void IsoStreamImpl::ReceiveInboundPacket(pw::span<const std::byte> packet) {
416   size_t packet_size = packet.size();
417   auto packet_view = pw::bluetooth::emboss::MakeIsoDataFramePacketView(
418       packet.data(), packet.size());
419   if (!packet_view.Ok()) {
420     bt_log(ERROR,
421            "iso",
422            "Incoming ISO frame failed consistency checks - ignoring");
423     return;
424   }
425 
426   size_t data_total_length = packet_view.header().data_total_length().Read();
427   size_t header_size =
428       pw::bluetooth::emboss::IsoDataFrameHeaderView::SizeInBytes();
429   size_t packet_actual_size = data_total_length + header_size;
430 
431   // This condition should have been caught by Emboss
432   PW_CHECK(packet_size >= packet_actual_size,
433            "Packet too short to hold data specified in header");
434 
435   // Truncate any extra data at end of packet
436   if (packet_size > packet_actual_size) {
437     packet = packet.subspan(0, packet_actual_size);
438   }
439 
440   inbound_assembler_.ProcessNext(packet);
441 }
442 
HandleCompletePacket(const pw::span<const std::byte> & packet)443 void IsoStreamImpl::HandleCompletePacket(
444     const pw::span<const std::byte>& packet) {
445   if (!on_incoming_data_available_cb_) {
446     bt_log(WARN,
447            "iso",
448            "Incoming data received for stream whose data path has not yet been "
449            "set up - ignoring");
450     return;
451   }
452 
453   if (inbound_client_is_waiting_) {
454     inbound_client_is_waiting_ = false;
455     if (on_incoming_data_available_cb_(packet)) {
456       // Packet was processed successfully - we're done here
457       return;
458     }
459     // This is not a hard error, but it is a bit unusual and probably worth
460     // noting.
461     bt_log(INFO,
462            "iso",
463            "ISO incoming packet client previously requested packets, now not "
464            "accepting new ones");
465   }
466 
467   // Client not ready to handle packet, queue it up until they ask for it
468   incoming_data_queue_.push(
469       std::make_unique<IsoDataPacket>(packet.begin(), packet.end()));
470 }
471 
BuildPacketForSending(const pw::span<const std::byte> & data,pw::bluetooth::emboss::IsoDataPbFlag pb_flag,std::optional<SduHeaderInfo> sdu_header,std::optional<uint32_t> time_stamp)472 DynamicByteBuffer IsoStreamImpl::BuildPacketForSending(
473     const pw::span<const std::byte>& data,
474     pw::bluetooth::emboss::IsoDataPbFlag pb_flag,
475     std::optional<SduHeaderInfo> sdu_header,
476     std::optional<uint32_t> time_stamp) {
477   PW_CHECK((pb_flag == pw::bluetooth::emboss::IsoDataPbFlag::FIRST_FRAGMENT ||
478             pb_flag == pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU) ==
479                sdu_header.has_value(),
480            "Header required for first and complete fragments.");
481 
482   DynamicByteBuffer packet(TotalPacketSize(
483       time_stamp.has_value(), sdu_header.has_value(), data.size()));
484   auto view = pw::bluetooth::emboss::MakeIsoDataFramePacketView(
485       packet.mutable_data(), packet.size());
486 
487   view.header().connection_handle().Write(cis_hci_handle_);
488   view.header().pb_flag().Write(pb_flag);
489   view.header().ts_flag().Write(
490       time_stamp.has_value()
491           ? pw::bluetooth::emboss::TsFlag::TIMESTAMP_PRESENT
492           : pw::bluetooth::emboss::TsFlag::TIMESTAMP_NOT_PRESENT);
493   view.header().data_total_length().Write(TotalDataLength(
494       time_stamp.has_value(), sdu_header.has_value(), data.size()));
495 
496   if (time_stamp.has_value()) {
497     view.time_stamp().Write(*time_stamp);
498   }
499 
500   if (sdu_header.has_value()) {
501     view.packet_sequence_number().Write(sdu_header->packet_sequence_number);
502     view.iso_sdu_length().Write(sdu_header->iso_sdu_length);
503     // This flag is RFU when sending to controller, the valid data flag is all
504     // zeros as required (see BT Core spec v5.4, Vol 4, Part E, Sec 5.4.5).
505     view.packet_status_flag().Write(
506         pw::bluetooth::emboss::IsoDataPacketStatus::VALID_DATA);
507   }
508 
509   memcpy(view.iso_sdu_fragment().BackingStorage().data(),
510          data.data(),
511          data.size());
512 
513   return packet;
514 }
515 
ReadNextQueuedIncomingPacket()516 std::unique_ptr<IsoDataPacket> IsoStreamImpl::ReadNextQueuedIncomingPacket() {
517   if (incoming_data_queue_.empty()) {
518     inbound_client_is_waiting_ = true;
519     return nullptr;
520   }
521 
522   std::unique_ptr<IsoDataPacket> packet =
523       std::move(incoming_data_queue_.front());
524   incoming_data_queue_.pop();
525   return packet;
526 }
527 
Send(pw::ConstByteSpan data)528 void IsoStreamImpl::Send(pw::ConstByteSpan data) {
529   PW_CHECK(data_channel_, "Send called while not registered to a data stream.");
530   PW_CHECK(data.size() <= std::numeric_limits<uint16_t>::max());
531 
532   const size_t max_length = data_channel_->buffer_info().max_data_length();
533   std::optional<SduHeaderInfo> sdu_header = SduHeaderInfo{
534       // TODO: https://pwbug.dev/393366531 - Implement sequence number.
535       .packet_sequence_number = 0,
536       .iso_sdu_length = static_cast<uint16_t>(data.size()),
537   };
538 
539   // Fragmentation loop.
540   while (!data.empty()) {
541     size_t length_remaining =
542         TotalDataLength(/*has_timestamp=*/false,
543                         /*has_sdu_header=*/sdu_header.has_value(),
544                         /*data_size=*/data.size());
545     // This is the first fragment if we haven't sent the SDU header yet.
546     const bool is_first = sdu_header.has_value();
547     // This is the last fragment if there is sufficient buffer space.
548     const bool is_last = length_remaining <= max_length;
549 
550     pw::bluetooth::emboss::IsoDataPbFlag flag;
551     if (is_first && is_last) {
552       flag = pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU;
553     } else if (is_first && !is_last) {
554       flag = pw::bluetooth::emboss::IsoDataPbFlag::FIRST_FRAGMENT;
555     } else if (!is_first && is_last) {
556       flag = pw::bluetooth::emboss::IsoDataPbFlag::LAST_FRAGMENT;
557     } else if (!is_first && !is_last) {
558       flag = pw::bluetooth::emboss::IsoDataPbFlag::INTERMEDIATE_FRAGMENT;
559     } else {
560       PW_UNREACHABLE;
561     }
562 
563     // Send the largest possible fragment, reduce `data` to remaining span.
564     pw::ConstByteSpan fragment;
565     size_t fragment_length = FragmentDataLength(false, is_first, max_length);
566     std::tie(fragment, data) = SplitSpan(data, fragment_length);
567     data_channel_->SendData(BuildPacketForSending(fragment, flag, sdu_header));
568     sdu_header.reset();
569   }
570 }
571 
Close()572 void IsoStreamImpl::Close() { on_closed_cb_(); }
573 
Create(uint8_t cig_id,uint8_t cis_id,hci_spec::ConnectionHandle cis_handle,CisEstablishedCallback on_established_cb,hci::CommandChannel::WeakPtr cmd,pw::Callback<void ()> on_closed_cb,hci::IsoDataChannel * data_channel)574 std::unique_ptr<IsoStream> IsoStream::Create(
575     uint8_t cig_id,
576     uint8_t cis_id,
577     hci_spec::ConnectionHandle cis_handle,
578     CisEstablishedCallback on_established_cb,
579     hci::CommandChannel::WeakPtr cmd,
580     pw::Callback<void()> on_closed_cb,
581     hci::IsoDataChannel* data_channel) {
582   return std::make_unique<IsoStreamImpl>(cig_id,
583                                          cis_id,
584                                          cis_handle,
585                                          std::move(on_established_cb),
586                                          std::move(cmd),
587                                          std::move(on_closed_cb),
588                                          data_channel);
589 }
590 
591 }  // namespace bt::iso
592