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_sapphire/internal/host/hci-spec/util.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci/sequential_command_runner.h"
19 #include "pw_bluetooth_sapphire/internal/host/iso/iso_inbound_packet_assembler.h"
20
21 namespace bt::iso {
22
23 class IsoStreamImpl final : public IsoStream {
24 public:
25 IsoStreamImpl(uint8_t cig_id,
26 uint8_t cis_id,
27 hci_spec::ConnectionHandle cis_handle,
28 CisEstablishedCallback on_established_cb,
29 hci::CommandChannel::WeakPtr cmd,
30 pw::Callback<void()> on_closed_cb);
31
32 // IsoStream overrides
33 bool OnCisEstablished(const hci::EventPacket& event) override;
34 void SetupDataPath(
35 pw::bluetooth::emboss::DataPathDirection direction,
36 const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>& codec_id,
37 const std::optional<std::vector<uint8_t>>& codec_configuration,
38 uint32_t controller_delay_usecs,
39 SetupDataPathCallback&& on_complete_cb,
40 IncomingDataHandler&& on_incoming_data_available_cb) override;
cis_handle() const41 hci_spec::ConnectionHandle cis_handle() const override {
42 return cis_hci_handle_;
43 }
44 void Close() override;
45 std::unique_ptr<IsoDataPacket> ReadNextQueuedIncomingPacket() override;
GetWeakPtr()46 IsoStream::WeakPtr GetWeakPtr() override { return weak_self_.GetWeakPtr(); }
47
48 // IsoDataChannel::ConnectionInterface override
49 void ReceiveInboundPacket(pw::span<const std::byte> packet) override;
50
51 private:
52 void HandleCompletePacket(const pw::span<const std::byte>& packet);
53
54 enum class IsoStreamState {
55 kNotEstablished,
56 kEstablished,
57 } state_;
58
59 uint8_t cig_id_ __attribute__((unused));
60 uint8_t cis_id_ __attribute__((unused));
61
62 // Connection parameters, only valid after CIS is established
63 CisEstablishedParameters cis_params_;
64
65 // Handle assigned by the controller
66 hci_spec::ConnectionHandle cis_hci_handle_;
67
68 // Called after HCI_LE_CIS_Established event is received and handled
69 CisEstablishedCallback cis_established_cb_;
70
71 IsoInboundPacketAssembler inbound_assembler_;
72
73 IncomingDataHandler on_incoming_data_available_cb_;
74
75 // When true, we will send a notification to the client when the next packet
76 // arrives. Otherwise, we will just queue it up.
77 bool inbound_client_is_waiting_ = false;
78
79 std::queue<std::unique_ptr<std::vector<std::byte>>> incoming_data_queue_;
80
81 // Called when stream is closed
82 pw::Callback<void()> on_closed_cb_;
83
84 // Has the data path been configured?
85 enum class DataPathState {
86 kNotSetUp,
87 kSettingUp,
88 kSetUp,
89 };
90 DataPathState input_data_path_state_ = DataPathState::kNotSetUp;
91 DataPathState output_data_path_state_ = DataPathState::kNotSetUp;
92
93 hci::CommandChannel::WeakPtr cmd_;
94
95 hci::CommandChannel::EventHandlerId cis_established_handler_;
96
97 WeakSelf<IsoStreamImpl> weak_self_;
98
99 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(IsoStreamImpl);
100 };
101
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)102 IsoStreamImpl::IsoStreamImpl(uint8_t cig_id,
103 uint8_t cis_id,
104 hci_spec::ConnectionHandle cis_handle,
105 CisEstablishedCallback on_established_cb,
106 hci::CommandChannel::WeakPtr cmd,
107 pw::Callback<void()> on_closed_cb)
108 : IsoStream(),
109 state_(IsoStreamState::kNotEstablished),
110 cig_id_(cig_id),
111 cis_id_(cis_id),
112 cis_hci_handle_(cis_handle),
113 cis_established_cb_(std::move(on_established_cb)),
114 inbound_assembler_(
115 fit::bind_member<&IsoStreamImpl::HandleCompletePacket>(this)),
116 on_closed_cb_(std::move(on_closed_cb)),
117 cmd_(std::move(cmd)),
118 weak_self_(this) {
119 PW_CHECK(cmd_.is_alive());
120
121 auto weak_self = weak_self_.GetWeakPtr();
122 cis_established_handler_ = cmd_->AddLEMetaEventHandler(
123 hci_spec::kLECISEstablishedSubeventCode,
124 [self = std::move(weak_self)](const hci::EventPacket& event) {
125 if (!self.is_alive()) {
126 return hci::CommandChannel::EventCallbackResult::kRemove;
127 }
128 if (self->OnCisEstablished(event)) {
129 self->cis_established_handler_ = 0u;
130 return hci::CommandChannel::EventCallbackResult::kRemove;
131 }
132 return hci::CommandChannel::EventCallbackResult::kContinue;
133 });
134 PW_CHECK(cis_established_handler_ != 0u);
135 }
136
OnCisEstablished(const hci::EventPacket & event)137 bool IsoStreamImpl::OnCisEstablished(const hci::EventPacket& event) {
138 PW_CHECK(event.event_code() == hci_spec::kLEMetaEventCode);
139 PW_CHECK(event.view<pw::bluetooth::emboss::LEMetaEventView>()
140 .subevent_code()
141 .Read() == hci_spec::kLECISEstablishedSubeventCode);
142 auto view = event.view<pw::bluetooth::emboss::LECISEstablishedSubeventView>();
143
144 // Ignore any events intended for another CIS
145 hci_spec::ConnectionHandle handle = view.connection_handle().Read();
146 if (handle != cis_hci_handle_) {
147 bt_log(
148 INFO,
149 "iso",
150 "Ignoring CIS established notification for handle 0x%x (target: 0x%x)",
151 handle,
152 cis_hci_handle_);
153
154 // Event not handled
155 return false;
156 }
157
158 pw::bluetooth::emboss::StatusCode status = view.status().Read();
159 bt_log(INFO,
160 "iso",
161 "Handling CIS established notification for handle 0x%x (status: %s)",
162 handle,
163 hci_spec::StatusCodeToString(status).c_str());
164
165 if (status != pw::bluetooth::emboss::StatusCode::SUCCESS) {
166 cis_established_cb_(status, std::nullopt, std::nullopt);
167 Close();
168 return true;
169 }
170
171 state_ = IsoStreamState::kEstablished;
172
173 // General stream attributes
174 cis_params_.cig_sync_delay = view.cig_sync_delay().Read();
175 cis_params_.cis_sync_delay = view.cis_sync_delay().Read();
176 cis_params_.max_subevents = view.nse().Read();
177 cis_params_.iso_interval = view.iso_interval().Read();
178
179 // Central => Peripheral stream attributes
180 CisEstablishedParameters::CisUnidirectionalParams* params =
181 &cis_params_.c_to_p_params;
182 params->transport_latency = view.transport_latency_c_to_p().Read();
183 params->phy = view.phy_c_to_p().Read();
184 params->burst_number = view.bn_c_to_p().Read();
185 params->flush_timeout = view.ft_c_to_p().Read();
186 params->max_pdu_size = view.max_pdu_c_to_p().Read();
187
188 // Peripheral => Central stream attributes
189 params = &cis_params_.p_to_c_params;
190 params->transport_latency = view.transport_latency_p_to_c().Read();
191 params->phy = view.phy_p_to_c().Read();
192 params->burst_number = view.bn_p_to_c().Read();
193 params->flush_timeout = view.ft_p_to_c().Read();
194 params->max_pdu_size = view.max_pdu_p_to_c().Read();
195
196 cis_established_cb_(status, GetWeakPtr(), cis_params_);
197
198 // Event handled
199 return true;
200 }
201
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)202 void IsoStreamImpl::SetupDataPath(
203 pw::bluetooth::emboss::DataPathDirection direction,
204 const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>& codec_id,
205 const std::optional<std::vector<uint8_t>>& codec_configuration,
206 uint32_t controller_delay_usecs,
207 SetupDataPathCallback&& on_complete_cb,
208 IncomingDataHandler&& on_incoming_data_available_cb) {
209 if (state_ != IsoStreamState::kEstablished) {
210 bt_log(WARN, "iso", "failed to setup data path - CIS not established");
211 on_complete_cb(kCisNotEstablished);
212 return;
213 }
214
215 DataPathState* target_data_path_state;
216 const char* direction_as_str;
217 switch (direction) {
218 case pw::bluetooth::emboss::DataPathDirection::INPUT:
219 target_data_path_state = &input_data_path_state_;
220 direction_as_str = "Input";
221 break;
222 case pw::bluetooth::emboss::DataPathDirection::OUTPUT:
223 target_data_path_state = &output_data_path_state_;
224 direction_as_str = "Output";
225 break;
226 default:
227 bt_log(WARN,
228 "iso",
229 "invalid data path direction (%u)",
230 static_cast<unsigned>(direction));
231 on_complete_cb(kInvalidArgs);
232 return;
233 }
234
235 if (*target_data_path_state != DataPathState::kNotSetUp) {
236 bt_log(WARN,
237 "iso",
238 "attempt to setup %s CIS path - already setup",
239 direction_as_str);
240 on_complete_cb(kStreamAlreadyExists);
241 return;
242 }
243
244 bt_log(INFO, "iso", "setting up CIS data path for %s", direction_as_str);
245 size_t packet_size =
246 pw::bluetooth::emboss::LESetupISODataPathCommand::MinSizeInBytes() +
247 (codec_configuration.has_value() ? codec_configuration->size() : 0);
248 auto cmd_packet = hci::CommandPacket::New<
249 pw::bluetooth::emboss::LESetupISODataPathCommandWriter>(
250 hci_spec::kLESetupISODataPath, packet_size);
251 auto cmd_view = cmd_packet.view_t();
252 cmd_view.connection_handle().Write(cis_hci_handle_);
253 cmd_view.data_path_direction().Write(direction);
254 cmd_view.data_path_id().Write(0);
255 cmd_view.codec_id().CopyFrom(
256 const_cast<bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>&>(
257 codec_id)
258 .view());
259 cmd_view.controller_delay().Write(controller_delay_usecs);
260 if (codec_configuration.has_value()) {
261 cmd_view.codec_configuration_length().Write(codec_configuration->size());
262 std::memcpy(cmd_view.codec_configuration().BackingStorage().data(),
263 codec_configuration->data(),
264 codec_configuration->size());
265 } else {
266 cmd_view.codec_configuration_length().Write(0);
267 }
268
269 *target_data_path_state = DataPathState::kSettingUp;
270 WeakSelf<IsoStreamImpl>::WeakPtr self = weak_self_.GetWeakPtr();
271
272 bt_log(INFO, "iso", "sending LE_Setup_ISO_Data_Path command");
273 cmd_->SendCommand(
274 std::move(cmd_packet),
275 [on_complete_callback = std::move(on_complete_cb),
276 self,
277 cis_handle = cis_hci_handle_,
278 target_data_path_state,
279 direction,
280 on_incoming_data_available_callback =
281 std::move(on_incoming_data_available_cb)](
282 auto, const hci::EventPacket& cmd_complete) mutable {
283 if (!self.is_alive()) {
284 on_complete_callback(kStreamClosed);
285 return;
286 }
287
288 auto return_params =
289 cmd_complete.view<pw::bluetooth::emboss::
290 LESetupISODataPathCommandCompleteEventView>();
291 pw::bluetooth::emboss::StatusCode status =
292 return_params.status().Read();
293 hci_spec::ConnectionHandle connection_handle =
294 return_params.connection_handle().Read();
295
296 if (status != pw::bluetooth::emboss::StatusCode::SUCCESS) {
297 bt_log(ERROR,
298 "iso",
299 "failed to setup ISO data path for handle 0x%x (status: 0x%x)",
300 connection_handle,
301 static_cast<uint8_t>(status));
302 *target_data_path_state = DataPathState::kNotSetUp;
303 on_complete_callback(kStreamRejectedByController);
304 return;
305 }
306
307 // It's hard to know what is the right thing to do here. The controller
308 // accepted our request, but we don't agree on the connection handle ID.
309 // Something is amiss, so we will refuse to consider the data path
310 // setup even though the controller may think otherwise.
311 if (connection_handle != cis_handle) {
312 bt_log(ERROR,
313 "iso",
314 "handle mismatch in ISO data path setup completion (expected: "
315 "0x%x, actual: %x)",
316 cis_handle,
317 connection_handle);
318 *target_data_path_state = DataPathState::kNotSetUp;
319 on_complete_callback(kStreamRejectedByController);
320 return;
321 }
322
323 // Note that |direction| is a spec-defined value of dataflow direction
324 // relative to the controller, so this may look backwards.
325 if (direction == pw::bluetooth::emboss::DataPathDirection::OUTPUT) {
326 self->on_incoming_data_available_cb_ =
327 std::move(on_incoming_data_available_callback);
328 }
329 *target_data_path_state = DataPathState::kSetUp;
330 bt_log(INFO, "iso", "successfully set up data path");
331 on_complete_callback(kSuccess);
332 });
333 }
334
ReceiveInboundPacket(pw::span<const std::byte> packet)335 void IsoStreamImpl::ReceiveInboundPacket(pw::span<const std::byte> packet) {
336 inbound_assembler_.ProcessNext(packet);
337 }
338
HandleCompletePacket(const pw::span<const std::byte> & packet)339 void IsoStreamImpl::HandleCompletePacket(
340 const pw::span<const std::byte>& packet) {
341 if (!on_incoming_data_available_cb_) {
342 bt_log(WARN,
343 "iso",
344 "Incoming data received for stream whose data path has not yet been "
345 "set up - ignoring");
346 return;
347 }
348
349 if (inbound_client_is_waiting_) {
350 inbound_client_is_waiting_ = false;
351 if (on_incoming_data_available_cb_(packet)) {
352 // Packet was processed successfully - we're done here
353 return;
354 }
355 // This is not a hard error, but it is a bit unusual and probably worth
356 // noting.
357 bt_log(INFO,
358 "iso",
359 "ISO incoming packet client previously requested packets, now not "
360 "accepting new ones");
361 }
362
363 // Client not ready to handle packet, queue it up until they ask for it
364 incoming_data_queue_.push(
365 std::make_unique<IsoDataPacket>(packet.begin(), packet.end()));
366 }
367
ReadNextQueuedIncomingPacket()368 std::unique_ptr<IsoDataPacket> IsoStreamImpl::ReadNextQueuedIncomingPacket() {
369 if (incoming_data_queue_.empty()) {
370 inbound_client_is_waiting_ = true;
371 return nullptr;
372 }
373
374 std::unique_ptr<IsoDataPacket> packet =
375 std::move(incoming_data_queue_.front());
376 incoming_data_queue_.pop();
377 return packet;
378 }
379
Close()380 void IsoStreamImpl::Close() { on_closed_cb_(); }
381
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)382 std::unique_ptr<IsoStream> IsoStream::Create(
383 uint8_t cig_id,
384 uint8_t cis_id,
385 hci_spec::ConnectionHandle cis_handle,
386 CisEstablishedCallback on_established_cb,
387 hci::CommandChannel::WeakPtr cmd,
388 pw::Callback<void()> on_closed_cb) {
389 return std::make_unique<IsoStreamImpl>(cig_id,
390 cis_id,
391 cis_handle,
392 std::move(on_established_cb),
393 std::move(cmd),
394 std::move(on_closed_cb));
395 }
396
397 } // namespace bt::iso
398