xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/iso/iso_stream.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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