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