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