xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/controllers/fidl_controller.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/fuchsia/host/controllers/fidl_controller.h"
16 
17 #include "pw_bluetooth_sapphire/fuchsia/host/controllers/helpers.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "zircon/status.h"
22 
23 namespace bt::controllers {
24 
25 namespace fhbt = fuchsia_hardware_bluetooth;
26 using ReceivedPacket = fhbt::ReceivedPacket;
27 
28 namespace {
VendorFeaturesToFeaturesBits(fhbt::VendorFeatures features)29 pw::bluetooth::Controller::FeaturesBits VendorFeaturesToFeaturesBits(
30     fhbt::VendorFeatures features) {
31   pw::bluetooth::Controller::FeaturesBits out{0};
32   if (features.acl_priority_command().has_value() &&
33       features.acl_priority_command()) {
34     out |= pw::bluetooth::Controller::FeaturesBits::kSetAclPriorityCommand;
35   }
36   if (features.android_vendor_extensions().has_value()) {
37     // Ignore the content of android_vendor_extension field now.
38     out |= pw::bluetooth::Controller::FeaturesBits::kAndroidVendorExtensions;
39   }
40   return out;
41 }
42 
AclPriorityToFidl(pw::bluetooth::AclPriority priority)43 fhbt::VendorAclPriority AclPriorityToFidl(pw::bluetooth::AclPriority priority) {
44   switch (priority) {
45     case pw::bluetooth::AclPriority::kNormal:
46       return fhbt::VendorAclPriority::kNormal;
47     case pw::bluetooth::AclPriority::kSource:
48     case pw::bluetooth::AclPriority::kSink:
49       return fhbt::VendorAclPriority::kHigh;
50   }
51 }
52 
AclPriorityToFidlAclDirection(pw::bluetooth::AclPriority priority)53 fhbt::VendorAclDirection AclPriorityToFidlAclDirection(
54     pw::bluetooth::AclPriority priority) {
55   switch (priority) {
56     // The direction for kNormal is arbitrary.
57     case pw::bluetooth::AclPriority::kNormal:
58     case pw::bluetooth::AclPriority::kSource:
59       return fhbt::VendorAclDirection::kSource;
60     case pw::bluetooth::AclPriority::kSink:
61       return fhbt::VendorAclDirection::kSink;
62   }
63 }
64 
ScoCodingFormatToFidl(pw::bluetooth::Controller::ScoCodingFormat coding_format)65 fhbt::ScoCodingFormat ScoCodingFormatToFidl(
66     pw::bluetooth::Controller::ScoCodingFormat coding_format) {
67   switch (coding_format) {
68     case pw::bluetooth::Controller::ScoCodingFormat::kCvsd:
69       return fhbt::ScoCodingFormat::kCvsd;
70     case pw::bluetooth::Controller::ScoCodingFormat::kMsbc:
71       return fhbt::ScoCodingFormat::kMsbc;
72     default:
73       BT_PANIC("invalid SCO coding format");
74   }
75 }
76 
ScoEncodingToFidl(pw::bluetooth::Controller::ScoEncoding encoding)77 fhbt::ScoEncoding ScoEncodingToFidl(
78     pw::bluetooth::Controller::ScoEncoding encoding) {
79   switch (encoding) {
80     case pw::bluetooth::Controller::ScoEncoding::k8Bits:
81       return fhbt::ScoEncoding::kBits8;
82     case pw::bluetooth::Controller::ScoEncoding::k16Bits:
83       return fhbt::ScoEncoding::kBits16;
84     default:
85       BT_PANIC("invalid SCO encoding");
86   }
87 }
88 
ScoSampleRateToFidl(pw::bluetooth::Controller::ScoSampleRate sample_rate)89 fhbt::ScoSampleRate ScoSampleRateToFidl(
90     pw::bluetooth::Controller::ScoSampleRate sample_rate) {
91   switch (sample_rate) {
92     case pw::bluetooth::Controller::ScoSampleRate::k8Khz:
93       return fhbt::ScoSampleRate::kKhz8;
94     case pw::bluetooth::Controller::ScoSampleRate::k16Khz:
95       return fhbt::ScoSampleRate::kKhz16;
96     default:
97       BT_PANIC("invalid SCO sample rate");
98   }
99 }
100 
101 }  // namespace
102 
VendorEventHandler(std::function<void (zx_status_t)> unbind_callback)103 VendorEventHandler::VendorEventHandler(
104     std::function<void(zx_status_t)> unbind_callback)
105     : unbind_callback_(std::move(unbind_callback)) {}
106 
handle_unknown_event(fidl::UnknownEventMetadata<fhbt::Vendor> metadata)107 void VendorEventHandler::handle_unknown_event(
108     fidl::UnknownEventMetadata<fhbt::Vendor> metadata) {
109   bt_log(WARN,
110          "controllers",
111          "Unknown event from Vendor server: %lu",
112          metadata.event_ordinal);
113 }
114 
on_fidl_error(fidl::UnbindInfo error)115 void VendorEventHandler::on_fidl_error(fidl::UnbindInfo error) {
116   bt_log(ERROR,
117          "controllers",
118          "Vendor protocol closed: %s",
119          error.FormatDescription().c_str());
120   unbind_callback_(ZX_ERR_PEER_CLOSED);
121 }
122 
HciEventHandler(std::function<void (zx_status_t)> unbind_callback,std::function<void (fuchsia_hardware_bluetooth::ReceivedPacket)> on_receive_callback)123 HciEventHandler::HciEventHandler(
124     std::function<void(zx_status_t)> unbind_callback,
125     std::function<void(fuchsia_hardware_bluetooth::ReceivedPacket)>
126         on_receive_callback)
127     : on_receive_callback_(std::move(on_receive_callback)),
128       unbind_callback_(std::move(unbind_callback)) {}
129 
OnReceive(fuchsia_hardware_bluetooth::ReceivedPacket & packet)130 void HciEventHandler::OnReceive(
131     fuchsia_hardware_bluetooth::ReceivedPacket& packet) {
132   on_receive_callback_(std::move(packet));
133 }
134 
handle_unknown_event(fidl::UnknownEventMetadata<fhbt::HciTransport> metadata)135 void HciEventHandler::handle_unknown_event(
136     fidl::UnknownEventMetadata<fhbt::HciTransport> metadata) {
137   bt_log(WARN,
138          "controllers",
139          "Unknown event from Hci server: %lu",
140          metadata.event_ordinal);
141 }
142 
on_fidl_error(fidl::UnbindInfo error)143 void HciEventHandler::on_fidl_error(fidl::UnbindInfo error) {
144   bt_log(ERROR,
145          "controllers",
146          "Hci protocol closed: %s",
147          error.FormatDescription().c_str());
148   unbind_callback_(ZX_ERR_PEER_CLOSED);
149 }
150 
FidlController(fidl::ClientEnd<fhbt::Vendor> vendor_client_end,async_dispatcher_t * dispatcher)151 FidlController::FidlController(fidl::ClientEnd<fhbt::Vendor> vendor_client_end,
152                                async_dispatcher_t* dispatcher)
153     : vendor_event_handler_([this](zx_status_t status) { OnError(status); }),
__anon95b2019c0302(zx_status_t status) 154       hci_event_handler_([this](zx_status_t status) { OnError(status); },
__anon95b2019c0402(fhbt::ReceivedPacket packet) 155                          [this](fhbt::ReceivedPacket packet) {
156                            OnReceive(std::move(packet));
157                          }),
158       sco_event_handler_(
__anon95b2019c0502(zx_status_t status) 159           [this](zx_status_t status) { OnScoUnbind(status); },
__anon95b2019c0602(fhbt::ScoPacket packet) 160           [this](fhbt::ScoPacket packet) { OnReceiveSco(std::move(packet)); }),
161       dispatcher_(dispatcher) {
162   PW_CHECK(vendor_client_end.is_valid());
163   vendor_client_end_ = std::move(vendor_client_end);
164 }
165 
~FidlController()166 FidlController::~FidlController() { CleanUp(); }
167 
Initialize(PwStatusCallback complete_callback,PwStatusCallback error_callback)168 void FidlController::Initialize(PwStatusCallback complete_callback,
169                                 PwStatusCallback error_callback) {
170   initialize_complete_cb_ = std::move(complete_callback);
171   error_cb_ = std::move(error_callback);
172 
173   vendor_ = fidl::Client<fhbt::Vendor>(
174       std::move(vendor_client_end_), dispatcher_, &vendor_event_handler_);
175 
176   // Connect to Hci protocol
177   vendor_->OpenHciTransport().Then(
178       [this](fidl::Result<fhbt::Vendor::OpenHciTransport>& result) {
179         if (!result.is_ok()) {
180           bt_log(ERROR,
181                  "bt-host",
182                  "Failed to open HciTransport: %s",
183                  result.error_value().FormatDescription().c_str());
184           if (result.error_value().is_framework_error()) {
185             OnError(result.error_value().framework_error().status());
186           } else {
187             OnError(result.error_value().domain_error());
188           }
189           return;
190         }
191 
192         InitializeHci(std::move(result->channel()));
193       });
194 }
195 
InitializeHci(fidl::ClientEnd<fhbt::HciTransport> hci_client_end)196 void FidlController::InitializeHci(
197     fidl::ClientEnd<fhbt::HciTransport> hci_client_end) {
198   hci_ = fidl::Client<fhbt::HciTransport>(
199       std::move(hci_client_end), dispatcher_, &hci_event_handler_);
200 
201   initialize_complete_cb_(PW_STATUS_OK);
202 }
203 
Close(PwStatusCallback callback)204 void FidlController::Close(PwStatusCallback callback) {
205   CleanUp();
206   callback(PW_STATUS_OK);
207 }
208 
SendCommand(pw::span<const std::byte> command)209 void FidlController::SendCommand(pw::span<const std::byte> command) {
210   BufferView view = BufferView(command);
211   fidl::VectorView vec_view = fidl::VectorView<uint8_t>::FromExternal(
212       const_cast<uint8_t*>(view.data()), view.size());
213   fidl::ObjectView obj_view =
214       fidl::ObjectView<fidl::VectorView<uint8_t>>::FromExternal(&vec_view);
215   // Use Wire bindings to avoid copying `command`.
216   hci_.wire()
217       ->Send(fhbt::wire::SentPacket::WithCommand(obj_view))
218       .Then([this](fidl::WireUnownedResult<fhbt::HciTransport::Send>& result) {
219         if (!result.ok()) {
220           bt_log(ERROR,
221                  "controllers",
222                  "failed to send command: %s",
223                  result.status_string());
224           OnError(result.status());
225           return;
226         }
227       });
228 }
229 
SendAclData(pw::span<const std::byte> data)230 void FidlController::SendAclData(pw::span<const std::byte> data) {
231   BufferView view = BufferView(data);
232   fidl::VectorView vec_view = fidl::VectorView<uint8_t>::FromExternal(
233       const_cast<uint8_t*>(view.data()), view.size());
234   fidl::ObjectView obj_view =
235       fidl::ObjectView<fidl::VectorView<uint8_t>>::FromExternal(&vec_view);
236   // Use Wire bindings to avoid copying `data`.
237   hci_.wire()
238       ->Send(fhbt::wire::SentPacket::WithAcl(obj_view))
239       .Then([this](fidl::WireUnownedResult<fhbt::HciTransport::Send>& result) {
240         if (!result.ok()) {
241           bt_log(ERROR,
242                  "controllers",
243                  "failed to send ACL: %s",
244                  result.status_string());
245           OnError(result.status());
246           return;
247         }
248       });
249 }
250 
SendScoData(pw::span<const std::byte> data)251 void FidlController::SendScoData(pw::span<const std::byte> data) {
252   if (!sco_connection_) {
253     bt_log(ERROR,
254            "controllers",
255            "SendScoData() called when SCO is not configured");
256     OnError(ZX_ERR_BAD_STATE);
257     return;
258   }
259   BufferView view = BufferView(data);
260   fidl::VectorView vec_view = fidl::VectorView<uint8_t>::FromExternal(
261       const_cast<uint8_t*>(view.data()), view.size());
262   // Use Wire bindings to avoid copying `data`.
263   sco_connection_.value().wire()->Send(vec_view).Then(
264       [this](fidl::WireUnownedResult<fhbt::ScoConnection::Send>& result) {
265         if (!result.ok()) {
266           bt_log(ERROR,
267                  "controllers",
268                  "failed to send SCO: %s",
269                  result.status_string());
270           OnError(result.status());
271           return;
272         }
273       });
274 }
275 
SendIsoData(pw::span<const std::byte> data)276 void FidlController::SendIsoData(pw::span<const std::byte> data) {
277   BufferView view = BufferView(data);
278   fidl::VectorView vec_view = fidl::VectorView<uint8_t>::FromExternal(
279       const_cast<uint8_t*>(view.data()), view.size());
280   fidl::ObjectView obj_view =
281       fidl::ObjectView<fidl::VectorView<uint8_t>>::FromExternal(&vec_view);
282   // Use Wire bindings to avoid copying `data`.
283   hci_.wire()
284       ->Send(fhbt::wire::SentPacket::WithIso(obj_view))
285       .Then([this](fidl::WireUnownedResult<fhbt::HciTransport::Send>& result) {
286         if (!result.ok()) {
287           bt_log(ERROR,
288                  "controllers",
289                  "failed to send ISO: %s",
290                  result.status_string());
291           OnError(result.status());
292           return;
293         }
294       });
295 }
296 
ConfigureSco(ScoCodingFormat coding_format,ScoEncoding encoding,ScoSampleRate sample_rate,PwStatusCallback callback)297 void FidlController::ConfigureSco(ScoCodingFormat coding_format,
298                                   ScoEncoding encoding,
299                                   ScoSampleRate sample_rate,
300                                   PwStatusCallback callback) {
301   if (sco_connection_) {
302     callback(pw::Status::AlreadyExists());
303     return;
304   }
305 
306   fidl::Request<fhbt::HciTransport::ConfigureSco> request;
307   request.coding_format() = ScoCodingFormatToFidl(coding_format);
308   request.encoding() = ScoEncodingToFidl(encoding);
309   request.sample_rate() = ScoSampleRateToFidl(sample_rate);
310 
311   auto [client_end, server_end] =
312       fidl::CreateEndpoints<fhbt::ScoConnection>().value();
313   request.connection() = std::move(server_end);
314   sco_connection_ =
315       fidl::Client(std::move(client_end), dispatcher_, &sco_event_handler_);
316 
317   fit::result<::fidl::OneWayError> result =
318       hci_->ConfigureSco(std::move(request));
319 
320   if (!result.is_ok()) {
321     bt_log(WARN,
322            "controllers",
323            "Failed to configure SCO: %s",
324            result.error_value().FormatDescription().c_str());
325     sco_connection_.reset();
326     callback(ZxStatusToPwStatus(result.error_value().status()));
327     return;
328   }
329 
330   callback(ZxStatusToPwStatus(ZX_OK));
331 }
332 
ResetSco(pw::Callback<void (pw::Status)> callback)333 void FidlController::ResetSco(pw::Callback<void(pw::Status)> callback) {
334   if (!sco_connection_) {
335     bt_log(WARN, "controllers", "ResetSco(): no SCO connection configured");
336     callback(pw::Status::FailedPrecondition());
337     return;
338   }
339   if (reset_sco_cb_) {
340     bt_log(WARN, "controllers", "ResetSco(): already pending", );
341     callback(pw::Status::AlreadyExists());
342     return;
343   }
344   reset_sco_cb_ = std::move(callback);
345 
346   fit::result<fidl::OneWayError> result = sco_connection_.value()->Stop();
347   if (result.is_error()) {
348     OnError(ZX_ERR_BAD_STATE);
349     return;
350   }
351 }
352 
GetFeatures(pw::Callback<void (FidlController::FeaturesBits)> callback)353 void FidlController::GetFeatures(
354     pw::Callback<void(FidlController::FeaturesBits)> callback) {
355   vendor_->GetFeatures().Then(
356       [this, cb = std::move(callback)](
357           fidl::Result<fhbt::Vendor::GetFeatures>& result) mutable {
358         if (result.is_error()) {
359           bt_log(WARN,
360                  "controllers",
361                  "GetFeatures(): %s",
362                  result.error_value().status_string());
363           OnError(ZX_ERR_BAD_STATE);
364           return;
365         }
366 
367         FidlController::FeaturesBits features_bits =
368             VendorFeaturesToFeaturesBits(result.value());
369         cb(features_bits);
370       });
371 }
372 
EncodeVendorCommand(pw::bluetooth::VendorCommandParameters parameters,pw::Callback<void (pw::Result<pw::span<const std::byte>>)> callback)373 void FidlController::EncodeVendorCommand(
374     pw::bluetooth::VendorCommandParameters parameters,
375     pw::Callback<void(pw::Result<pw::span<const std::byte>>)> callback) {
376   PW_CHECK(vendor_);
377 
378   if (!std::holds_alternative<pw::bluetooth::SetAclPriorityCommandParameters>(
379           parameters)) {
380     callback(pw::Status::Unimplemented());
381     return;
382   }
383 
384   pw::bluetooth::SetAclPriorityCommandParameters params =
385       std::get<pw::bluetooth::SetAclPriorityCommandParameters>(parameters);
386 
387   fhbt::VendorSetAclPriorityParams priority_params;
388   priority_params.connection_handle(params.connection_handle);
389   priority_params.priority(AclPriorityToFidl(params.priority));
390   priority_params.direction(AclPriorityToFidlAclDirection(params.priority));
391 
392   auto command = fhbt::VendorCommand::WithSetAclPriority(priority_params);
393 
394   vendor_->EncodeCommand(command).Then(
395       [cb = std::move(callback)](
396           fidl::Result<fhbt::Vendor::EncodeCommand>& result) mutable {
397         if (!result.is_ok()) {
398           bt_log(ERROR,
399                  "controllers",
400                  "Failed to encode vendor command: %s",
401                  result.error_value().FormatDescription().c_str());
402           if (result.error_value().is_framework_error()) {
403             cb(ZxStatusToPwStatus(
404                 result.error_value().framework_error().status()));
405           } else {
406             cb(ZxStatusToPwStatus(result.error_value().domain_error()));
407           }
408           return;
409         }
410         auto span = pw::as_bytes(pw::span(result->encoded()));
411         cb(span);
412       });
413 }
414 
OnReceive(fhbt::ScoPacket & packet)415 void FidlController::ScoEventHandler::OnReceive(fhbt::ScoPacket& packet) {
416   on_receive_callback_(std::move(packet));
417 }
418 
on_fidl_error(fidl::UnbindInfo error)419 void FidlController::ScoEventHandler::on_fidl_error(fidl::UnbindInfo error) {
420   bt_log(DEBUG,
421          "controllers",
422          "SCO protocol closed: %s",
423          error.FormatDescription().c_str());
424   unbind_callback_(ZX_ERR_PEER_CLOSED);
425 }
426 
handle_unknown_event(fidl::UnknownEventMetadata<fhbt::ScoConnection> metadata)427 void FidlController::ScoEventHandler::handle_unknown_event(
428     fidl::UnknownEventMetadata<fhbt::ScoConnection> metadata) {
429   bt_log(WARN,
430          "controllers",
431          "Unknown event from ScoConnection server: %lu",
432          metadata.event_ordinal);
433 }
434 
ScoEventHandler(pw::Function<void (zx_status_t)> unbind_callback,pw::Function<void (fuchsia_hardware_bluetooth::ScoPacket)> on_receive_callback)435 FidlController::ScoEventHandler::ScoEventHandler(
436     pw::Function<void(zx_status_t)> unbind_callback,
437     pw::Function<void(fuchsia_hardware_bluetooth::ScoPacket)>
438         on_receive_callback)
439     : on_receive_callback_(std::move(on_receive_callback)),
440       unbind_callback_(std::move(unbind_callback)) {}
441 
OnReceive(ReceivedPacket packet)442 void FidlController::OnReceive(ReceivedPacket packet) {
443   switch (packet.Which()) {
444     case ReceivedPacket::Tag::kEvent:
445       event_cb_(BufferView(packet.event().value()).subspan());
446       break;
447     case ReceivedPacket::Tag::kAcl:
448       acl_cb_(BufferView(packet.acl().value()).subspan());
449       break;
450     case ReceivedPacket::Tag::kIso:
451       iso_cb_(BufferView(packet.iso().value()).subspan());
452       break;
453     default:
454       bt_log(WARN,
455              "controllers",
456              "OnReceive: unknown packet type %lu",
457              static_cast<uint64_t>(packet.Which()));
458   }
459   fit::result<fidl::OneWayError> result = hci_->AckReceive();
460   if (result.is_error()) {
461     OnError(ZX_ERR_IO);
462   }
463 }
464 
OnReceiveSco(fuchsia_hardware_bluetooth::ScoPacket packet)465 void FidlController::OnReceiveSco(
466     fuchsia_hardware_bluetooth::ScoPacket packet) {
467   fit::result<fidl::OneWayError> result = sco_connection_.value()->AckReceive();
468   if (result.is_error()) {
469     OnError(ZX_ERR_IO);
470     return;
471   }
472   sco_cb_(BufferView(packet.packet()).subspan());
473 }
474 
OnScoUnbind(zx_status_t status)475 void FidlController::OnScoUnbind(zx_status_t status) {
476   // The server shouldn't close a ScoConnection on its own. It should only close
477   // after the host calls Stop().
478   if (!reset_sco_cb_) {
479     bt_log(ERROR,
480            "controllers",
481            "ScoConnection closed unexpectedly (Stop() not called): %s",
482            zx_status_get_string(status));
483     OnError(ZX_ERR_BAD_STATE);
484     return;
485   }
486   bt_log(DEBUG, "controllers", "ScoConnection closed");
487   sco_connection_.reset();
488   reset_sco_cb_(pw::OkStatus());
489   reset_sco_cb_ = nullptr;
490 }
491 
OnError(zx_status_t status)492 void FidlController::OnError(zx_status_t status) {
493   CleanUp();
494 
495   // If |initialize_complete_cb_| has already been called, then initialization
496   // is complete and we use |error_cb_|
497   if (initialize_complete_cb_) {
498     initialize_complete_cb_(ZxStatusToPwStatus(status));
499     // Clean up |error_cb_| since we only need one callback to be called during
500     // initialization.
501     error_cb_ = nullptr;
502   } else if (error_cb_) {
503     error_cb_(ZxStatusToPwStatus(status));
504   }
505 }
506 
CleanUp()507 void FidlController::CleanUp() {
508   if (shutting_down_) {
509     return;
510   }
511   shutting_down_ = true;
512 
513   if (hci_) {
514     auto hci_endpoint = hci_.UnbindMaybeGetEndpoint();
515   }
516   if (vendor_) {
517     auto vendor_endpoint = vendor_.UnbindMaybeGetEndpoint();
518   }
519   if (sco_connection_) {
520     auto sco_endpoint = sco_connection_->UnbindMaybeGetEndpoint();
521     sco_connection_.reset();
522   }
523 }
524 
525 }  // namespace bt::controllers
526