• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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