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 <pw_assert/check.h>
21 #include <zircon/processargs.h>
22
23 #include "fidl/fuchsia.bluetooth.host/cpp/fidl.h"
24 #include "fidl/fuchsia.hardware.bluetooth/cpp/fidl.h"
25 #include "fidl/fuchsia.scheduler/cpp/fidl.h"
26 #include "host.h"
27 #include "lib/component/incoming/cpp/protocol.h"
28 #include "pw_bluetooth_sapphire/fuchsia/bt_host/bt_host_config.h"
29 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
30 #include "pw_log/log.h"
31 #include "util.h"
32
33 using InitCallback = fit::callback<void(bool success)>;
34 using ErrorCallback = fit::callback<void()>;
35
36 const std::string OUTGOING_SERVICE_NAME = "fuchsia.bluetooth.host.Host";
37 const std::string THREAD_ROLE_NAME = "fuchsia.bluetooth.host.thread";
38 const std::string VMAR_ROLE_NAME = "fuchsia.bluetooth.host.memory";
39
40 class LifecycleHandler
41 : public fuchsia::process::lifecycle::Lifecycle,
42 public fidl::AsyncEventHandler<fuchsia_bluetooth_host::Receiver> {
43 public:
44 using WeakPtr = WeakSelf<bthost::BtHostComponent>::WeakPtr;
45
LifecycleHandler(async::Loop * loop,WeakPtr host)46 explicit LifecycleHandler(async::Loop* loop, WeakPtr host)
47 : loop_(loop), host_(std::move(host)) {
48 // Get the PA_LIFECYCLE handle, and instantiate the channel with it
49 zx::channel channel = zx::channel(zx_take_startup_handle(PA_LIFECYCLE));
50 // Bind to the channel and start listening for events
51 bindings_.AddBinding(
52 this,
53 fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle>(
54 std::move(channel)),
55 loop_->dispatcher());
56 }
57
58 // Schedule a shut down
PostStopTask()59 void PostStopTask() {
60 // Verify we don't already have a Stop task scheduled
61 if (shutting_down_) {
62 return;
63 }
64 shutting_down_ = true;
65
66 async::PostTask(loop_->dispatcher(), [this]() { Stop(); });
67 }
68
69 // Shut down immediately
Stop()70 void Stop() override {
71 host_->ShutDown();
72 loop_->Shutdown();
73 bindings_.CloseAll();
74 }
75
76 // AsyncEventHandler overrides
on_fidl_error(fidl::UnbindInfo error)77 void on_fidl_error(fidl::UnbindInfo error) override {
78 bt_log(WARN, "bt-host", "Receiver interface disconnected");
79 Stop();
80 }
81
handle_unknown_event(fidl::UnknownEventMetadata<fuchsia_bluetooth_host::Receiver> metadata)82 void handle_unknown_event(
83 fidl::UnknownEventMetadata<fuchsia_bluetooth_host::Receiver> metadata)
84 override {
85 bt_log(WARN,
86 "bt-host",
87 "Received an unknown event with ordinal %lu",
88 metadata.event_ordinal);
89 }
90
91 private:
92 async::Loop* loop_;
93 WeakPtr host_;
94 fidl::BindingSet<fuchsia::process::lifecycle::Lifecycle> bindings_;
95 bool shutting_down_ = false;
96 };
97
SetHandleRole(fidl::SyncClient<fuchsia_scheduler::RoleManager> & role_manager,fuchsia_scheduler::RoleTarget target,const std::string & role_name)98 void SetHandleRole(
99 fidl::SyncClient<fuchsia_scheduler::RoleManager>& role_manager,
100 fuchsia_scheduler::RoleTarget target,
101 const std::string& role_name) {
102 bt_log(DEBUG, "bt-host", "Setting role %s", role_name.c_str());
103 fuchsia_scheduler::RoleManagerSetRoleRequest request;
104 request.target(std::move(target))
105 .role(fuchsia_scheduler::RoleName(role_name));
106 auto set_role_res = role_manager->SetRole(std::move(request));
107 if (!set_role_res.is_ok()) {
108 std::string err_str = set_role_res.error_value().FormatDescription();
109 bt_log(WARN,
110 "bt-host",
111 "Couldn't set role %s: %s",
112 role_name.c_str(),
113 err_str.c_str());
114 return;
115 }
116 bt_log(INFO, "bt-host", "Set role %s successfully.", role_name.c_str());
117 }
118
SetRoles()119 void SetRoles() {
120 bt_log(DEBUG, "bt-host", "Connecting to RoleManager");
121 auto client_end_res = component::Connect<fuchsia_scheduler::RoleManager>();
122 if (!client_end_res.is_ok()) {
123 bt_log(WARN,
124 "bt-host",
125 "Couldn't connect to RoleManager: %s",
126 client_end_res.status_string());
127 return;
128 }
129 fidl::SyncClient<fuchsia_scheduler::RoleManager> role_manager(
130 std::move(*client_end_res));
131
132 bt_log(DEBUG, "bt-host", "Cloning self thread");
133 zx::thread thread_self;
134 zx_status_t thread_status =
135 zx::thread::self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &thread_self);
136 if (thread_status == ZX_OK) {
137 SetHandleRole(
138 role_manager,
139 fuchsia_scheduler::RoleTarget::WithThread(std::move(thread_self)),
140 THREAD_ROLE_NAME);
141 } else {
142 zx::result<> err = zx::error(thread_status);
143 bt_log(ERROR,
144 "bt-host",
145 "Couldn't clone self thread for profile: %s",
146 err.status_string());
147 }
148
149 bt_log(DEBUG, "bt-host", "Cloning root vmar");
150 zx::vmar root_vmar;
151 zx_status_t vmar_status =
152 zx::vmar::root_self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &root_vmar);
153 if (vmar_status == ZX_OK) {
154 SetHandleRole(role_manager,
155 fuchsia_scheduler::RoleTarget::WithVmar(std::move(root_vmar)),
156 VMAR_ROLE_NAME);
157 } else {
158 zx::result<> err = zx::error(vmar_status);
159 bt_log(ERROR,
160 "bt-host",
161 "Couldn't clone self thread for profile: %s",
162 err.status_string());
163 }
164 }
165
main()166 int main() {
167 async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
168 pw::log_fuchsia::InitializeLogging(loop.dispatcher());
169
170 bt_log(INFO, "bt-host", "Starting bt-host");
171 SetRoles();
172
173 bt_host_config::Config config =
174 bt_host_config::Config::TakeFromStartupHandle();
175 if (config.device_path().empty()) {
176 bt_log(ERROR, "bt-host", "device_path is empty! Can't open. Quitting.");
177 return 1;
178 }
179 bt_log(INFO, "bt-host", "device_path: %s", config.device_path().c_str());
180
181 std::unique_ptr<bthost::BtHostComponent> host =
182 bthost::BtHostComponent::Create(loop.dispatcher(), config.device_path());
183
184 LifecycleHandler lifecycle_handler(&loop, host->GetWeakPtr());
185
186 auto init_cb = [&host, &lifecycle_handler, &loop](bool success) {
187 PW_DCHECK(host);
188 if (!success) {
189 bt_log(
190 ERROR, "bt-host", "Failed to initialize bt-host; shutting down...");
191 lifecycle_handler.Stop();
192 return;
193 }
194 bt_log(DEBUG, "bt-host", "bt-host initialized; starting FIDL servers...");
195
196 // Bind current host to Host protocol interface
197 auto endpoints = fidl::CreateEndpoints<fuchsia_bluetooth_host::Host>();
198 if (endpoints.is_error()) {
199 bt_log(ERROR,
200 "bt-host",
201 "Couldn't create endpoints: %d",
202 endpoints.error_value());
203 lifecycle_handler.Stop();
204 return;
205 }
206 host->BindToHostInterface(std::move(endpoints->server));
207
208 // Add Host device and protocol to bt-gap via Receiver
209 zx::result receiver_client =
210 component::Connect<fuchsia_bluetooth_host::Receiver>();
211 if (!receiver_client.is_ok()) {
212 bt_log(ERROR,
213 "bt-host",
214 "Error connecting to the Receiver protocol: %s",
215 receiver_client.status_string());
216 lifecycle_handler.Stop();
217 return;
218 }
219 fidl::Client client(
220 std::move(*receiver_client), loop.dispatcher(), &lifecycle_handler);
221 fit::result<fidl::Error> result =
222 client->AddHost(fuchsia_bluetooth_host::ReceiverAddHostRequest(
223 std::move(endpoints->client)));
224 if (!result.is_ok()) {
225 bt_log(ERROR,
226 "bt-host",
227 "Failed to add host: %s",
228 result.error_value().FormatDescription().c_str());
229 lifecycle_handler.Stop();
230 return;
231 }
232 };
233
234 auto error_cb = [&lifecycle_handler]() {
235 // The controller has reported an error. Shut down after the currently
236 // scheduled tasks finish executing.
237 bt_log(WARN, "bt-host", "Error in bt-host; shutting down...");
238 lifecycle_handler.PostStopTask();
239 };
240
241 zx::result<fidl::ClientEnd<fuchsia_hardware_bluetooth::Vendor>>
242 vendor_client_end_result =
243 bthost::CreateVendorHandle(config.device_path());
244 if (!vendor_client_end_result.is_ok()) {
245 bt_log(ERROR,
246 "bt-host",
247 "Failed to create VendorHandle; cannot initialize bt-host: %s",
248 vendor_client_end_result.status_string());
249 return 1;
250 }
251
252 bool initialize_res =
253 host->Initialize(std::move(vendor_client_end_result.value()),
254 init_cb,
255 error_cb,
256 config.legacy_pairing_enabled());
257 if (!initialize_res) {
258 bt_log(ERROR, "bt-host", "Error initializing bt-host; shutting down...");
259 return 1;
260 }
261
262 loop.Run();
263 return 0;
264 }
265