• 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/fuchsia/host/fidl/iso_stream_server.h"
16 
17 #include <lib/fidl/cpp/wire/channel.h>
18 #include <pw_assert/check.h>
19 #include <pw_bluetooth/hci_data.emb.h>
20 
21 #include <cinttypes>
22 
23 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
24 
25 namespace bthost {
26 
IsoStreamServer(fidl::InterfaceRequest<fuchsia::bluetooth::le::IsochronousStream> request,fit::callback<void ()> on_closed_cb)27 IsoStreamServer::IsoStreamServer(
28     fidl::InterfaceRequest<fuchsia::bluetooth::le::IsochronousStream> request,
29     fit::callback<void()> on_closed_cb)
30     : ServerBase(this, std::move(request)),
31       on_closed_cb_(std::move(on_closed_cb)),
32       weak_self_(this) {
33   set_error_handler([this](zx_status_t) { OnClosed(); });
34 }
35 
OnStreamEstablished(bt::iso::IsoStream::WeakPtr stream_ptr,const bt::iso::CisEstablishedParameters & connection_params)36 void IsoStreamServer::OnStreamEstablished(
37     bt::iso::IsoStream::WeakPtr stream_ptr,
38     const bt::iso::CisEstablishedParameters& connection_params) {
39   bt_log(INFO, "fidl", "CIS established");
40   iso_stream_ = stream_ptr;
41   fuchsia::bluetooth::le::IsochronousStreamOnEstablishedRequest request;
42   request.set_result(ZX_OK);
43   fuchsia::bluetooth::le::CisEstablishedParameters params =
44       bthost::fidl_helpers::CisEstablishedParametersToFidl(connection_params);
45   request.set_established_params(std::move(params));
46   binding()->events().OnEstablished(std::move(request));
47 }
48 
OnStreamEstablishmentFailed(pw::bluetooth::emboss::StatusCode status)49 void IsoStreamServer::OnStreamEstablishmentFailed(
50     pw::bluetooth::emboss::StatusCode status) {
51   PW_CHECK(status != pw::bluetooth::emboss::StatusCode::SUCCESS);
52   bt_log(WARN,
53          "fidl",
54          "CIS failed to be established: %u",
55          static_cast<unsigned>(status));
56   fuchsia::bluetooth::le::IsochronousStreamOnEstablishedRequest request;
57   request.set_result(ZX_ERR_INTERNAL);
58   binding()->events().OnEstablished(std::move(request));
59 }
60 
SetupDataPath(fuchsia::bluetooth::le::IsochronousStreamSetupDataPathRequest parameters,SetupDataPathCallback fidl_cb)61 void IsoStreamServer::SetupDataPath(
62     fuchsia::bluetooth::le::IsochronousStreamSetupDataPathRequest parameters,
63     SetupDataPathCallback fidl_cb) {
64   pw::bluetooth::emboss::DataPathDirection direction =
65       fidl_helpers::DataPathDirectionFromFidl(parameters.data_direction());
66   const char* direction_as_str =
67       fidl_helpers::DataPathDirectionToString(direction);
68   bt_log(INFO,
69          "fidl",
70          "Request received to set up data path (direction: %s)",
71          direction_as_str);
72   if (direction != pw::bluetooth::emboss::DataPathDirection::OUTPUT) {
73     // We only support Controller => Host at the moment
74     bt_log(WARN,
75            "fidl",
76            "Attempt to set up data path with unsupported direction: %s",
77            direction_as_str);
78     fidl_cb(fpromise::error(ZX_ERR_NOT_SUPPORTED));
79     return;
80   }
81 
82   bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter> codec_id =
83       fidl_helpers::CodecIdFromFidl(parameters.codec_attributes().codec_id());
84   std::optional<std::vector<uint8_t>> codec_configuration;
85   if (parameters.codec_attributes().has_codec_configuration()) {
86     codec_configuration = parameters.codec_attributes().codec_configuration();
87   }
88 
89   zx::duration delay(parameters.controller_delay());
90   uint32_t delay_in_us = delay.to_usecs();
91   if (!iso_stream_.has_value()) {
92     bt_log(WARN, "fidl", "data path setup failed (CIS not established)");
93     fidl_cb(fpromise::error(ZX_ERR_BAD_STATE));
94     return;
95   }
96   if (!iso_stream_->is_alive()) {
97     bt_log(INFO, "fidl", "Attempt to set data path after CIS closed");
98     fidl_cb(fpromise::error(ZX_ERR_BAD_STATE));
99     return;
100   }
101 
102   auto on_setup_complete_cb =
103       [fidl_cb =
104            std::move(fidl_cb)](bt::iso::IsoStream::SetupDataPathError error) {
105         switch (error) {
106           case bt::iso::IsoStream::kSuccess:
107             bt_log(INFO, "fidl", "data path successfully setup");
108             fidl_cb(fpromise::ok());
109             break;
110           case bt::iso::IsoStream::kStreamAlreadyExists:
111             bt_log(
112                 WARN, "fidl", "data path setup failed (stream already setup)");
113             fidl_cb(fpromise::error(ZX_ERR_ALREADY_EXISTS));
114             break;
115           case bt::iso::IsoStream::kCisNotEstablished:
116             bt_log(
117                 WARN, "fidl", "data path setup failed (CIS not established)");
118             fidl_cb(fpromise::error(ZX_ERR_BAD_STATE));
119             break;
120           case bt::iso::IsoStream::kInvalidArgs:
121             bt_log(WARN, "fidl", "data path setup failed (invalid parameters)");
122             fidl_cb(fpromise::error(ZX_ERR_INVALID_ARGS));
123             break;
124           case bt::iso::IsoStream::kStreamClosed:
125             bt_log(WARN, "fidl", "data path setup failed (stream closed)");
126             fidl_cb(fpromise::error(ZX_ERR_BAD_STATE));
127             break;
128           default:
129             bt_log(ERROR,
130                    "fidl",
131                    "Unsupported case in SetupDataPathError: %u",
132                    static_cast<unsigned>(error));
133             fidl_cb(fpromise::error(ZX_ERR_INTERNAL));
134             break;
135         }
136       };
137   (*iso_stream_)
138       ->SetupDataPath(
139           direction,
140           codec_id,
141           codec_configuration,
142           delay_in_us,
143           std::move(on_setup_complete_cb),
144           fit::bind_member<&IsoStreamServer::OnIncomingDataAvailable>(this));
145 }
146 
SendIncomingPacket(pw::span<const std::byte> packet)147 void IsoStreamServer::SendIncomingPacket(pw::span<const std::byte> packet) {
148   auto view = pw::bluetooth::emboss::MakeIsoDataFramePacketView(packet.data(),
149                                                                 packet.size());
150   if (!view.Ok()) {
151     bt_log(ERROR, "fidl", "Failed to parse ISO data frame");
152     // Hanging get will remain unfulfilled
153     return;
154   }
155   PW_CHECK(view.header().pb_flag().Read() ==
156                pw::bluetooth::emboss::IsoDataPbFlag::COMPLETE_SDU,
157            "Incomplete SDU received from IsoStream");
158   fuchsia::bluetooth::le::IsochronousStream_Read_Response response;
159 
160   size_t data_fragment_size = view.sdu_fragment_size().Read();
161   std::vector<std::uint8_t> data_as_vector(data_fragment_size);
162 
163   std::memcpy(data_as_vector.data(),
164               view.iso_sdu_fragment().BackingStorage().data(),
165               data_fragment_size);
166   response.set_data(data_as_vector);
167   response.set_sequence_number(view.packet_sequence_number().Read());
168   response.set_status_flag(fidl_helpers::EmbossIsoPacketStatusFlagToFidl(
169       view.packet_status_flag().Read()));
170 
171   PW_CHECK(hanging_read_cb_);
172   hanging_read_cb_(
173       fuchsia::bluetooth::le::IsochronousStream_Read_Result::WithResponse(
174           std::move(response)));
175   hanging_read_cb_ = nullptr;
176 }
177 
OnIncomingDataAvailable(pw::span<const std::byte> packet)178 bool IsoStreamServer::OnIncomingDataAvailable(
179     pw::span<const std::byte> packet) {
180   if (!hanging_read_cb_) {
181     // This is not a hard error, but it is a bit suspicious and worth noting. We
182     // should not receive a notification of incoming data unless we have a
183     // hanging Read() operation.
184     bt_log(WARN,
185            "fidl",
186            "Notification of incoming data received with no outstanding read "
187            "operation");
188     return false;
189   }
190   SendIncomingPacket(packet);
191   return true;
192 }
193 
Read(ReadCallback callback)194 void IsoStreamServer::Read(ReadCallback callback) {
195   // We should not have more than one outstanding Read()
196   if (hanging_read_cb_) {
197     Close(ZX_ERR_BAD_STATE);
198     return;
199   }
200 
201   hanging_read_cb_ = std::move(callback);
202 
203   if (iso_stream_.has_value() && iso_stream_->is_alive()) {
204     std::unique_ptr<bt::iso::IsoDataPacket> packet =
205         (*iso_stream_)->ReadNextQueuedIncomingPacket();
206     if (packet) {
207       pw::span<const std::byte> packet_as_span(
208           static_cast<std::byte*>(packet->data()), packet->size());
209       SendIncomingPacket(packet_as_span);
210       return;
211     }
212   }
213 }
214 
OnClosed()215 void IsoStreamServer::OnClosed() {
216   if (iso_stream_.has_value() && iso_stream_->is_alive()) {
217     (*iso_stream_)->Close();
218   }
219   // This may free our instance.
220   on_closed_cb_();
221 }
222 
Close(zx_status_t epitaph)223 void IsoStreamServer::Close(zx_status_t epitaph) {
224   binding()->Close(epitaph);
225   OnClosed();
226 }
227 
handle_unknown_method(uint64_t ordinal,bool has_response)228 void IsoStreamServer::handle_unknown_method(uint64_t ordinal,
229                                             bool has_response) {
230   bt_log(WARN,
231          "fidl",
232          "Received unknown fidl call %#" PRIx64 " (%s responses)",
233          ordinal,
234          has_response ? "with" : "without");
235 }
236 
237 }  // namespace bthost
238