• 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 "controller.h"
16 
17 #include <lib/driver/component/cpp/driver_export.h>
18 #include <pw_assert/check.h>
19 
20 #include <memory>
21 #include <string>
22 
23 namespace bt_hci_virtual {
24 
VirtualController(fdf::DriverStartArgs start_args,fdf::UnownedSynchronizedDispatcher driver_dispatcher)25 VirtualController::VirtualController(
26     fdf::DriverStartArgs start_args,
27     fdf::UnownedSynchronizedDispatcher driver_dispatcher)
28     : DriverBase("bt_hci_virtual",
29                  std::move(start_args),
30                  std::move(driver_dispatcher)),
31       node_(fidl::WireClient(std::move(node()), dispatcher())),
32       devfs_connector_(fit::bind_member<&VirtualController::Connect>(this)) {}
33 
Start()34 zx::result<> VirtualController::Start() {
35   zx::result connector = devfs_connector_.Bind(dispatcher());
36   if (connector.is_error()) {
37     FDF_LOG(ERROR,
38             "Failed to bind devfs connecter to dispatcher: %u",
39             connector.status_value());
40     return connector.take_error();
41   }
42 
43   fidl::Arena args_arena;
44   // TODO: https://pwbug.dev/303503457 - Access virtual device via
45   // "/dev/class/bt-hci-virtual"
46   auto devfs = fuchsia_driver_framework::wire::DevfsAddArgs::Builder(args_arena)
47                    .connector(std::move(connector.value()))
48                    .class_name("sys/platform/bt-hci-emulator")
49                    .Build();
50   auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(args_arena)
51                   .name("bt_hci_virtual")
52                   .devfs_args(devfs)
53                   .Build();
54 
55   // Add bt_hci_virtual child node
56   AddVirtualControllerChildNode(args);
57 
58   return zx::ok();
59 }
60 
CreateEmulator(CreateEmulatorCompleter::Sync & completer)61 void VirtualController::CreateEmulator(
62     CreateEmulatorCompleter::Sync& completer) {
63   std::string name = "emulator";
64 
65   emulator_device_ = std::make_unique<EmulatorDevice>();
66 
67   auto add_child_cb = [this](auto args) {
68     FDF_LOG(INFO, "EmulatorDevice successfully initialized");
69     AddEmulatorChildNode(args, emulator_device_.get());
70   };
71   auto shutdown_cb = [this]() {
72     FDF_LOG(INFO, "Releasing EmulatorDevice");
73     emulator_device_.reset();
74   };
75 
76   zx_status_t status = emulator_device_->Initialize(
77       std::string_view(name), std::move(add_child_cb), std::move(shutdown_cb));
78 
79   if (status != ZX_OK) {
80     FDF_LOG(ERROR, "Failed to bind: %s\n", zx_status_get_string(status));
81     emulator_device_->Shutdown();
82     auto _ = emulator_node_controller_->Remove();
83     completer.ReplyError(status);
84     return;
85   }
86   emulator_device_->set_emulator_child_node(std::move(emulator_child_node_));
87   completer.ReplySuccess(
88       fidl::StringView::FromExternal(name.data(), name.size()));
89 }
90 
CreateLoopbackDevice(CreateLoopbackDeviceRequestView request,CreateLoopbackDeviceCompleter::Sync & completer)91 void VirtualController::CreateLoopbackDevice(
92     CreateLoopbackDeviceRequestView request,
93     CreateLoopbackDeviceCompleter::Sync& completer) {
94   std::string name = "loopback";
95 
96   loopback_device_ = std::make_unique<LoopbackDevice>();
97 
98   PW_CHECK(request->has_uart_channel());
99   zx_status_t status = loopback_device_->Initialize(
100       std::move(request->uart_channel()),
101       std::string_view(name),
102       [this](auto args) {
103         // Add LoopbackDevice as a child node of VirtualController
104         FDF_LOG(INFO, "LoopbackDevice successfully initialized");
105         AddLoopbackChildNode(args);
106       });
107   if (status != ZX_OK) {
108     FDF_LOG(ERROR, "Failed to bind: %s\n", zx_status_get_string(status));
109     auto _ = loopback_node_controller_->Remove();
110     loopback_device_.reset();
111     return;
112   }
113 }
114 
handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::VirtualController> metadata,fidl::UnknownMethodCompleter::Sync & completer)115 void VirtualController::handle_unknown_method(
116     fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::VirtualController>
117         metadata,
118     fidl::UnknownMethodCompleter::Sync& completer) {
119   FDF_LOG(ERROR,
120           "Unknown method in VirtualController request, closing with "
121           "ZX_ERR_NOT_SUPPORTED");
122   completer.Close(ZX_ERR_NOT_SUPPORTED);
123 }
124 
Connect(fidl::ServerEnd<fuchsia_hardware_bluetooth::VirtualController> request)125 void VirtualController::Connect(
126     fidl::ServerEnd<fuchsia_hardware_bluetooth::VirtualController> request) {
127   virtual_controller_binding_group_.AddBinding(
128       dispatcher(), std::move(request), this, fidl::kIgnoreBindingClosure);
129 }
130 
AddVirtualControllerChildNode(fuchsia_driver_framework::wire::NodeAddArgs args)131 zx_status_t VirtualController::AddVirtualControllerChildNode(
132     fuchsia_driver_framework::wire::NodeAddArgs args) {
133   // Create the endpoints of fuchsia_driver_framework::NodeController protocol
134   auto controller_endpoints =
135       fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
136   if (controller_endpoints.is_error()) {
137     FDF_LOG(ERROR,
138             "Create node controller endpoints failed: %s",
139             controller_endpoints.status_string());
140     return controller_endpoints.error_value();
141   }
142 
143   // Create the endpoints of fuchsia_driver_framework::Node protocol for the
144   // child node, and hold the client end of it, because no driver will bind to
145   // the child node.
146   auto child_node_endpoints =
147       fidl::CreateEndpoints<fuchsia_driver_framework::Node>();
148   if (child_node_endpoints.is_error()) {
149     FDF_LOG(ERROR,
150             "Create child node endpoints failed: %s",
151             child_node_endpoints.status_string());
152     return child_node_endpoints.error_value();
153   }
154 
155   // Add a VirtualController child node
156   auto child_result =
157       node_.sync()->AddChild(std::move(args),
158                              std::move(controller_endpoints->server),
159                              std::move(child_node_endpoints->server));
160   if (!child_result.ok()) {
161     FDF_LOG(ERROR,
162             "Failed to add bt_hci_virtual child node, FIDL error: %s",
163             child_result.status_string());
164     return child_result.status();
165   }
166   if (child_result->is_error()) {
167     FDF_LOG(ERROR,
168             "Failed to add bt_hci_virtual child node: %u",
169             static_cast<uint32_t>(child_result->error_value()));
170     return ZX_ERR_INTERNAL;
171   }
172 
173   virtual_controller_child_node_.Bind(
174       std::move(child_node_endpoints->client), dispatcher(), this);
175   node_controller_.Bind(
176       std::move(controller_endpoints->client), dispatcher(), this);
177 
178   return ZX_OK;
179 }
180 
AddLoopbackChildNode(fuchsia_driver_framework::wire::NodeAddArgs args)181 zx_status_t VirtualController::AddLoopbackChildNode(
182     fuchsia_driver_framework::wire::NodeAddArgs args) {
183   // Create the endpoints of fuchsia_driver_framework::NodeController protocol
184   auto controller_endpoints =
185       fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
186   if (controller_endpoints.is_error()) {
187     FDF_LOG(ERROR,
188             "Create node controller endpoints failed: %s",
189             controller_endpoints.status_string());
190     return controller_endpoints.error_value();
191   }
192 
193   // Create the endpoints of fuchsia_driver_framework::Node protocol for the
194   // child node, and hold the client end of it, because no driver will bind to
195   // the child node.
196   auto child_node_endpoints =
197       fidl::CreateEndpoints<fuchsia_driver_framework::Node>();
198   if (child_node_endpoints.is_error()) {
199     FDF_LOG(ERROR,
200             "Create child node endpoints failed: %s",
201             child_node_endpoints.status_string());
202     return child_node_endpoints.error_value();
203   }
204 
205   // Add a loopback device as a child node of VirtualController
206   auto child_result =
207       node_.sync()->AddChild(std::move(args),
208                              std::move(controller_endpoints->server),
209                              std::move(child_node_endpoints->server));
210   if (!child_result.ok()) {
211     FDF_LOG(ERROR,
212             "Failed to add loopback device node, FIDL error: %s",
213             child_result.status_string());
214     return child_result.status();
215   }
216 
217   if (child_result->is_error()) {
218     FDF_LOG(ERROR,
219             "Failed to add loopback device node: %u",
220             static_cast<uint32_t>(child_result->error_value()));
221     return ZX_ERR_INTERNAL;
222   }
223 
224   // |loopback_child_node_| does not need to create more child nodes so we do
225   // not need an event_handler and we do not need to worry about it being
226   // re-bound
227   loopback_child_node_.Bind(std::move(child_node_endpoints->client),
228                             dispatcher());
229   loopback_node_controller_.Bind(
230       std::move(controller_endpoints->client), dispatcher(), this);
231 
232   return ZX_OK;
233 }
234 
AddEmulatorChildNode(fuchsia_driver_framework::wire::NodeAddArgs args,EmulatorDevice * emulator_device)235 zx_status_t VirtualController::AddEmulatorChildNode(
236     fuchsia_driver_framework::wire::NodeAddArgs args,
237     EmulatorDevice* emulator_device) {
238   // Create the endpoints of fuchsia_driver_framework::NodeController protocol
239   auto controller_endpoints =
240       fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
241   if (controller_endpoints.is_error()) {
242     FDF_LOG(ERROR,
243             "Create node controller endpoints failed: %s",
244             controller_endpoints.status_string());
245     return controller_endpoints.error_value();
246   }
247 
248   // Create the endpoints of fuchsia_driver_framework::Node protocol for the
249   // child node, and hold the client end of it, because no driver will bind to
250   // the child node.
251   auto child_node_endpoints =
252       fidl::CreateEndpoints<fuchsia_driver_framework::Node>();
253   if (child_node_endpoints.is_error()) {
254     FDF_LOG(ERROR,
255             "Create child node endpoints failed: %s",
256             child_node_endpoints.status_string());
257     return child_node_endpoints.error_value();
258   }
259 
260   // Add an emulator device as a child node of VirtualController
261   auto child_result =
262       node_.sync()->AddChild(std::move(args),
263                              std::move(controller_endpoints->server),
264                              std::move(child_node_endpoints->server));
265 
266   if (!child_result.ok()) {
267     FDF_LOG(ERROR,
268             "Failed to add emulator device node, FIDL error: %s",
269             child_result.status_string());
270     return child_result.status();
271   }
272 
273   if (child_result->is_error()) {
274     FDF_LOG(ERROR,
275             "Failed to add emulator device node: %u",
276             static_cast<uint32_t>(child_result->error_value()));
277     return ZX_ERR_INTERNAL;
278   }
279 
280   emulator_child_node_.Bind(
281       std::move(child_node_endpoints->client), dispatcher(), emulator_device);
282   emulator_node_controller_.Bind(
283       std::move(controller_endpoints->client), dispatcher(), this);
284 
285   return ZX_OK;
286 }
287 
288 }  // namespace bt_hci_virtual
289 
290 FUCHSIA_DRIVER_EXPORT(bt_hci_virtual::VirtualController);
291