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