| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/process/lifecycle/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fidl/cpp/binding_set.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <zircon/processargs.h> |
| |
| #include "fidl/fuchsia.bluetooth.host/cpp/fidl.h" |
| #include "fidl/fuchsia.hardware.bluetooth/cpp/fidl.h" |
| #include "fuchsia/hardware/bluetooth/cpp/fidl.h" |
| #include "host.h" |
| #include "lib/component/incoming/cpp/protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/bt_host_config.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h" |
| #include "util.h" |
| |
| using InitCallback = fit::callback<void(bool success)>; |
| using ErrorCallback = fit::callback<void()>; |
| |
| const std::string OUTGOING_SERVICE_NAME = "fuchsia.bluetooth.host.Host"; |
| |
| class LifecycleHandler : public fuchsia::process::lifecycle::Lifecycle, |
| public fidl::AsyncEventHandler<fuchsia_bluetooth_host::Receiver> { |
| public: |
| using WeakPtr = WeakSelf<bthost::BtHostComponent>::WeakPtr; |
| |
| explicit LifecycleHandler(async::Loop* loop, WeakPtr host) : loop_(loop), host_(std::move(host)) { |
| // Get the PA_LIFECYCLE handle, and instantiate the channel with it |
| zx::channel channel = zx::channel(zx_take_startup_handle(PA_LIFECYCLE)); |
| // Bind to the channel and start listening for events |
| bindings_.AddBinding( |
| this, fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle>(std::move(channel)), |
| loop_->dispatcher()); |
| } |
| |
| // Schedule a shut down |
| void PostStopTask() { |
| // Verify we don't already have a Stop task scheduled |
| if (shutting_down_) { |
| return; |
| } |
| shutting_down_ = true; |
| |
| async::PostTask(loop_->dispatcher(), [this]() { Stop(); }); |
| } |
| |
| // Shut down immediately |
| void Stop() override { |
| host_->ShutDown(); |
| loop_->Shutdown(); |
| bindings_.CloseAll(); |
| } |
| |
| // AsyncEventHandler overrides |
| void on_fidl_error(fidl::UnbindInfo error) override { |
| bt_log(WARN, "bt-host", "Receiver interface disconnected"); |
| Stop(); |
| } |
| |
| void handle_unknown_event( |
| fidl::UnknownEventMetadata<fuchsia_bluetooth_host::Receiver> metadata) override { |
| bt_log(WARN, "bt-host", "Received an unknown event with ordinal %lu", metadata.event_ordinal); |
| } |
| |
| private: |
| async::Loop* loop_; |
| WeakPtr host_; |
| fidl::BindingSet<fuchsia::process::lifecycle::Lifecycle> bindings_; |
| bool shutting_down_ = false; |
| }; |
| |
| int main() { |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); |
| |
| bt_log(INFO, "bt-host", "Starting bt-host"); |
| |
| bt_host_config::Config config = bt_host_config::Config::TakeFromStartupHandle(); |
| if (config.device_path().empty()) { |
| bt_log(ERROR, "bt-host", "device_path is empty! Can't open. Quitting."); |
| return 1; |
| } |
| bt_log(INFO, "bt-host", "device_path: %s", config.device_path().c_str()); |
| |
| std::unique_ptr<bthost::BtHostComponent> host = |
| bthost::BtHostComponent::Create(loop.dispatcher(), config.device_path()); |
| |
| LifecycleHandler lifecycle_handler(&loop, host->GetWeakPtr()); |
| |
| auto init_cb = [&host, &lifecycle_handler, &loop](bool success) { |
| BT_DEBUG_ASSERT(host); |
| if (!success) { |
| bt_log(ERROR, "bt-host", "Failed to initialize bt-host; shutting down..."); |
| lifecycle_handler.Stop(); |
| return; |
| } |
| bt_log(DEBUG, "bt-host", "bt-host initialized; starting FIDL servers..."); |
| |
| // Bind current host to Host protocol interface |
| auto endpoints = fidl::CreateEndpoints<fuchsia_bluetooth_host::Host>(); |
| if (endpoints.is_error()) { |
| bt_log(ERROR, "bt-host", "Couldn't create endpoints: %d", endpoints.error_value()); |
| lifecycle_handler.Stop(); |
| return; |
| } |
| host->BindToHostInterface(std::move(endpoints->server)); |
| |
| // Add Host device and protocol to bt-gap via Receiver |
| zx::result receiver_client = component::Connect<fuchsia_bluetooth_host::Receiver>(); |
| if (!receiver_client.is_ok()) { |
| bt_log(ERROR, "bt-host", "Error connecting to the Receiver protocol: %s", |
| receiver_client.status_string()); |
| lifecycle_handler.Stop(); |
| return; |
| } |
| fidl::Client client(std::move(*receiver_client), loop.dispatcher(), &lifecycle_handler); |
| fit::result<fidl::Error> result = client->AddHost( |
| fuchsia_bluetooth_host::ReceiverAddHostRequest(std::move(endpoints->client))); |
| if (!result.is_ok()) { |
| bt_log(ERROR, "bt-host", "Failed to add host: %s", |
| result.error_value().FormatDescription().c_str()); |
| lifecycle_handler.Stop(); |
| return; |
| } |
| }; |
| |
| auto error_cb = [&lifecycle_handler]() { |
| // The controller has reported an error. Shut down after the currently scheduled tasks finish |
| // executing. |
| bt_log(WARN, "bt-host", "Error in bt-host; shutting down..."); |
| lifecycle_handler.PostStopTask(); |
| }; |
| |
| fuchsia::hardware::bluetooth::VendorHandle vendor_handle = |
| bthost::CreateVendorHandle(config.device_path()); |
| if (!vendor_handle) { |
| bt_log(ERROR, "bt-host", "Failed to create VendorHandle; cannot initialize bt-host"); |
| return 1; |
| } |
| |
| bool initialize_res = host->Initialize(std::move(vendor_handle), init_cb, error_cb); |
| if (!initialize_res) { |
| bt_log(ERROR, "bt-host", "Error initializing bt-host; shutting down..."); |
| return 1; |
| } |
| |
| loop.Run(); |
| return 0; |
| } |