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