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 <fuchsia/process/lifecycle/cpp/fidl.h>
16 #include <lib/async-loop/cpp/loop.h>
17 #include <lib/async-loop/default.h>
18 #include <lib/fidl/cpp/binding_set.h>
19 #include <lib/sys/cpp/component_context.h>
20 #include <zircon/processargs.h>
21
22 #include "fidl/fuchsia.bluetooth.host/cpp/fidl.h"
23 #include "fidl/fuchsia.hardware.bluetooth/cpp/fidl.h"
24 #include "host.h"
25 #include "lib/component/incoming/cpp/protocol.h"
26 #include "pw_bluetooth_sapphire/fuchsia/bt_host/bt_host_config.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
28 #include "pw_log/log.h"
29 #include "util.h"
30
31 using InitCallback = fit::callback<void(bool success)>;
32 using ErrorCallback = fit::callback<void()>;
33
34 const std::string OUTGOING_SERVICE_NAME = "fuchsia.bluetooth.host.Host";
35
36 class LifecycleHandler
37 : public fuchsia::process::lifecycle::Lifecycle,
38 public fidl::AsyncEventHandler<fuchsia_bluetooth_host::Receiver> {
39 public:
40 using WeakPtr = WeakSelf<bthost::BtHostComponent>::WeakPtr;
41
LifecycleHandler(async::Loop * loop,WeakPtr host)42 explicit LifecycleHandler(async::Loop* loop, WeakPtr host)
43 : loop_(loop), host_(std::move(host)) {
44 // Get the PA_LIFECYCLE handle, and instantiate the channel with it
45 zx::channel channel = zx::channel(zx_take_startup_handle(PA_LIFECYCLE));
46 // Bind to the channel and start listening for events
47 bindings_.AddBinding(
48 this,
49 fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle>(
50 std::move(channel)),
51 loop_->dispatcher());
52 }
53
54 // Schedule a shut down
PostStopTask()55 void PostStopTask() {
56 // Verify we don't already have a Stop task scheduled
57 if (shutting_down_) {
58 return;
59 }
60 shutting_down_ = true;
61
62 async::PostTask(loop_->dispatcher(), [this]() { Stop(); });
63 }
64
65 // Shut down immediately
Stop()66 void Stop() override {
67 host_->ShutDown();
68 loop_->Shutdown();
69 bindings_.CloseAll();
70 }
71
72 // AsyncEventHandler overrides
on_fidl_error(fidl::UnbindInfo error)73 void on_fidl_error(fidl::UnbindInfo error) override {
74 bt_log(WARN, "bt-host", "Receiver interface disconnected");
75 Stop();
76 }
77
handle_unknown_event(fidl::UnknownEventMetadata<fuchsia_bluetooth_host::Receiver> metadata)78 void handle_unknown_event(
79 fidl::UnknownEventMetadata<fuchsia_bluetooth_host::Receiver> metadata)
80 override {
81 bt_log(WARN,
82 "bt-host",
83 "Received an unknown event with ordinal %lu",
84 metadata.event_ordinal);
85 }
86
87 private:
88 async::Loop* loop_;
89 WeakPtr host_;
90 fidl::BindingSet<fuchsia::process::lifecycle::Lifecycle> bindings_;
91 bool shutting_down_ = false;
92 };
93
main()94 int main() {
95 async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
96 pw::log_fuchsia::InitializeLogging(loop.dispatcher());
97
98 bt_log(INFO, "bt-host", "Starting bt-host");
99
100 bt_host_config::Config config =
101 bt_host_config::Config::TakeFromStartupHandle();
102 if (config.device_path().empty()) {
103 bt_log(ERROR, "bt-host", "device_path is empty! Can't open. Quitting.");
104 return 1;
105 }
106 bt_log(INFO, "bt-host", "device_path: %s", config.device_path().c_str());
107
108 std::unique_ptr<bthost::BtHostComponent> host =
109 bthost::BtHostComponent::Create(loop.dispatcher(), config.device_path());
110
111 LifecycleHandler lifecycle_handler(&loop, host->GetWeakPtr());
112
113 auto init_cb = [&host, &lifecycle_handler, &loop](bool success) {
114 PW_DCHECK(host);
115 if (!success) {
116 bt_log(
117 ERROR, "bt-host", "Failed to initialize bt-host; shutting down...");
118 lifecycle_handler.Stop();
119 return;
120 }
121 bt_log(DEBUG, "bt-host", "bt-host initialized; starting FIDL servers...");
122
123 // Bind current host to Host protocol interface
124 auto endpoints = fidl::CreateEndpoints<fuchsia_bluetooth_host::Host>();
125 if (endpoints.is_error()) {
126 bt_log(ERROR,
127 "bt-host",
128 "Couldn't create endpoints: %d",
129 endpoints.error_value());
130 lifecycle_handler.Stop();
131 return;
132 }
133 host->BindToHostInterface(std::move(endpoints->server));
134
135 // Add Host device and protocol to bt-gap via Receiver
136 zx::result receiver_client =
137 component::Connect<fuchsia_bluetooth_host::Receiver>();
138 if (!receiver_client.is_ok()) {
139 bt_log(ERROR,
140 "bt-host",
141 "Error connecting to the Receiver protocol: %s",
142 receiver_client.status_string());
143 lifecycle_handler.Stop();
144 return;
145 }
146 fidl::Client client(
147 std::move(*receiver_client), loop.dispatcher(), &lifecycle_handler);
148 fit::result<fidl::Error> result =
149 client->AddHost(fuchsia_bluetooth_host::ReceiverAddHostRequest(
150 std::move(endpoints->client)));
151 if (!result.is_ok()) {
152 bt_log(ERROR,
153 "bt-host",
154 "Failed to add host: %s",
155 result.error_value().FormatDescription().c_str());
156 lifecycle_handler.Stop();
157 return;
158 }
159 };
160
161 auto error_cb = [&lifecycle_handler]() {
162 // The controller has reported an error. Shut down after the currently
163 // scheduled tasks finish executing.
164 bt_log(WARN, "bt-host", "Error in bt-host; shutting down...");
165 lifecycle_handler.PostStopTask();
166 };
167
168 zx::result<fidl::ClientEnd<fuchsia_hardware_bluetooth::Vendor>>
169 vendor_client_end_result =
170 bthost::CreateVendorHandle(config.device_path());
171 if (!vendor_client_end_result.is_ok()) {
172 bt_log(ERROR,
173 "bt-host",
174 "Failed to create VendorHandle; cannot initialize bt-host: %s",
175 vendor_client_end_result.status_string());
176 return 1;
177 }
178
179 bool initialize_res =
180 host->Initialize(std::move(vendor_client_end_result.value()),
181 init_cb,
182 error_cb,
183 config.legacy_pairing_enabled());
184 if (!initialize_res) {
185 bt_log(ERROR, "bt-host", "Error initializing bt-host; shutting down...");
186 return 1;
187 }
188
189 loop.Run();
190 return 0;
191 }
192