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