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 "emulator.h"
16
17 #include <fidl/fuchsia.driver.framework/cpp/fidl.h>
18 #include <fidl/fuchsia.hardware.bluetooth/cpp/fidl.h>
19 #include <lib/driver/logging/cpp/logger.h>
20 #include <lib/fdf/cpp/dispatcher.h>
21
22 #include "emulated_peer.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
24 #include "pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
25 #include "pw_bluetooth_sapphire/internal/host/testing/fake_peer.h"
26
27 namespace fhbt = fuchsia_hardware_bluetooth;
28
29 using bt::DeviceAddress;
30 using bt::testing::FakeController;
31
32 namespace bt_hci_virtual {
33
34 namespace {
35
SettingsFromFidl(const fhbt::EmulatorSettings & input)36 FakeController::Settings SettingsFromFidl(const fhbt::EmulatorSettings& input) {
37 FakeController::Settings settings;
38 if (input.hci_config().has_value() &&
39 input.hci_config().value() == fhbt::HciConfig::kLeOnly) {
40 settings.ApplyLEOnlyDefaults();
41 } else {
42 settings.ApplyDualModeDefaults();
43 }
44
45 if (input.address().has_value()) {
46 settings.bd_addr =
47 DeviceAddress(DeviceAddress::Type::kBREDR, input.address()->bytes());
48 }
49
50 // TODO(armansito): Don't ignore "extended_advertising" setting when
51 // supported.
52 if (input.acl_buffer_settings().has_value()) {
53 fhbt::AclBufferSettings acl_settings = input.acl_buffer_settings().value();
54
55 if (acl_settings.data_packet_length().has_value()) {
56 settings.acl_data_packet_length =
57 acl_settings.data_packet_length().value();
58 }
59
60 if (acl_settings.total_num_data_packets().has_value()) {
61 settings.total_num_acl_data_packets =
62 acl_settings.total_num_data_packets().value();
63 }
64 }
65
66 if (input.le_acl_buffer_settings().has_value()) {
67 fhbt::AclBufferSettings le_acl_settings =
68 input.le_acl_buffer_settings().value();
69
70 if (le_acl_settings.data_packet_length().has_value()) {
71 settings.le_acl_data_packet_length =
72 le_acl_settings.data_packet_length().value();
73 }
74
75 if (le_acl_settings.total_num_data_packets().has_value()) {
76 settings.le_total_num_acl_data_packets =
77 le_acl_settings.total_num_data_packets().value();
78 }
79 }
80
81 return settings;
82 }
83
LeOwnAddressTypeToFidl(pw::bluetooth::emboss::LEOwnAddressType type)84 std::optional<fuchsia_bluetooth::AddressType> LeOwnAddressTypeToFidl(
85 pw::bluetooth::emboss::LEOwnAddressType type) {
86 std::optional<fuchsia_bluetooth::AddressType> res;
87
88 switch (type) {
89 case pw::bluetooth::emboss::LEOwnAddressType::PUBLIC:
90 case pw::bluetooth::emboss::LEOwnAddressType::PRIVATE_DEFAULT_TO_PUBLIC:
91 res.emplace(fuchsia_bluetooth::AddressType::kPublic);
92 return res;
93 case pw::bluetooth::emboss::LEOwnAddressType::RANDOM:
94 case pw::bluetooth::emboss::LEOwnAddressType::PRIVATE_DEFAULT_TO_RANDOM:
95 res.emplace(fuchsia_bluetooth::AddressType::kRandom);
96 return res;
97 }
98
99 ZX_PANIC("unsupported own address type");
100 return res;
101 }
102
103 } // namespace
104
EmulatorDevice()105 EmulatorDevice::EmulatorDevice()
106 : pw_dispatcher_(fdf::Dispatcher::GetCurrent()->async_dispatcher()),
107 fake_device_(pw_dispatcher_),
108 emulator_devfs_connector_(
109 fit::bind_member<&EmulatorDevice::ConnectEmulator>(this)),
110 vendor_devfs_connector_(
111 fit::bind_member<&EmulatorDevice::ConnectVendor>(this)) {}
112
~EmulatorDevice()113 EmulatorDevice::~EmulatorDevice() { fake_device_.Stop(); }
114
Initialize(std::string_view name,AddChildCallback callback,ShutdownCallback shutdown)115 zx_status_t EmulatorDevice::Initialize(std::string_view name,
116 AddChildCallback callback,
117 ShutdownCallback shutdown) {
118 shutdown_cb_ = std::move(shutdown);
119
120 bt::set_random_generator(&rng_);
121
122 // Initialize |fake_device_|
123 auto init_complete_cb = [](pw::Status status) {
124 if (!status.ok()) {
125 FDF_LOG(WARNING,
126 "FakeController failed to initialize: %s",
127 pw_StatusString(status));
128 }
129 };
130 auto error_cb = [this](pw::Status status) {
131 FDF_LOG(WARNING, "FakeController error: %s", pw_StatusString(status));
132 UnpublishHci();
133 };
134 fake_device_.Initialize(init_complete_cb, error_cb);
135
136 fake_device_.set_controller_parameters_callback(
137 fit::bind_member<&EmulatorDevice::OnControllerParametersChanged>(this));
138 fake_device_.set_advertising_state_callback(
139 fit::bind_member<&EmulatorDevice::OnLegacyAdvertisingStateChanged>(this));
140 fake_device_.set_connection_state_callback(
141 fit::bind_member<&EmulatorDevice::OnPeerConnectionStateChanged>(this));
142
143 // Create args to add emulator as a child node on behalf of VirtualController
144 zx::result connector = emulator_devfs_connector_.Bind(
145 fdf::Dispatcher::GetCurrent()->async_dispatcher());
146 if (connector.is_error()) {
147 FDF_LOG(ERROR,
148 "Failed to bind devfs connecter to dispatcher: %u",
149 connector.status_value());
150 return connector.error_value();
151 }
152
153 fidl::Arena args_arena;
154 auto devfs =
155 fuchsia_driver_framework::wire::DevfsAddArgs::Builder(args_arena)
156 .connector(std::move(connector.value()))
157 .connector_supports(fuchsia_device_fs::ConnectionType::kController)
158 .class_name("bt-emulator")
159 .Build();
160 auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(args_arena)
161 .name(name.data())
162 .devfs_args(devfs)
163 .Build();
164 callback(args);
165
166 return ZX_OK;
167 }
168
Shutdown()169 void EmulatorDevice::Shutdown() {
170 fake_device_.Stop();
171 peers_.clear();
172
173 if (shutdown_cb_) {
174 shutdown_cb_();
175 }
176 }
177
Publish(PublishRequest & request,PublishCompleter::Sync & completer)178 void EmulatorDevice::Publish(PublishRequest& request,
179 PublishCompleter::Sync& completer) {
180 if (hci_node_controller_.is_valid()) {
181 FDF_LOG(INFO, "bt-hci-device is already published");
182 completer.Reply(fit::error(fhbt::EmulatorError::kHciAlreadyPublished));
183 return;
184 }
185
186 FakeController::Settings settings = SettingsFromFidl(request);
187 fake_device_.set_settings(settings);
188
189 zx_status_t status = AddHciDeviceChildNode();
190 if (status != ZX_OK) {
191 FDF_LOG(WARNING, "Failed to publish bt-hci-device node");
192 completer.Reply(fit::error(fhbt::EmulatorError::kFailed));
193 } else {
194 FDF_LOG(INFO, "Successfully published bt-hci-device node");
195 completer.Reply(fit::success());
196 }
197 }
198
AddLowEnergyPeer(AddLowEnergyPeerRequest & request,AddLowEnergyPeerCompleter::Sync & completer)199 void EmulatorDevice::AddLowEnergyPeer(
200 AddLowEnergyPeerRequest& request,
201 AddLowEnergyPeerCompleter::Sync& completer) {
202 auto result = EmulatedPeer::NewLowEnergy(
203 std::move(request),
204 &fake_device_,
205 fdf::Dispatcher::GetCurrent()->async_dispatcher());
206 if (result.is_error()) {
207 completer.Reply(fit::error(result.error()));
208 return;
209 }
210
211 AddPeer(result.take_value());
212 completer.Reply(fit::success());
213 }
214
AddBredrPeer(AddBredrPeerRequest & request,AddBredrPeerCompleter::Sync & completer)215 void EmulatorDevice::AddBredrPeer(AddBredrPeerRequest& request,
216 AddBredrPeerCompleter::Sync& completer) {
217 auto result =
218 EmulatedPeer::NewBredr(std::move(request),
219 &fake_device_,
220 fdf::Dispatcher::GetCurrent()->async_dispatcher());
221 if (result.is_error()) {
222 completer.Reply(fit::error(result.error()));
223 return;
224 }
225
226 AddPeer(result.take_value());
227 completer.Reply(fit::success());
228 }
229
WatchControllerParameters(WatchControllerParametersCompleter::Sync & completer)230 void EmulatorDevice::WatchControllerParameters(
231 WatchControllerParametersCompleter::Sync& completer) {
232 controller_parameters_completer_.emplace(completer.ToAsync());
233 MaybeUpdateControllerParametersChanged();
234 }
235
WatchLeScanStates(WatchLeScanStatesCompleter::Sync & completer)236 void EmulatorDevice::WatchLeScanStates(
237 WatchLeScanStatesCompleter::Sync& completer) {}
238
WatchLegacyAdvertisingStates(WatchLegacyAdvertisingStatesCompleter::Sync & completer)239 void EmulatorDevice::WatchLegacyAdvertisingStates(
240 WatchLegacyAdvertisingStatesCompleter::Sync& completer) {
241 legacy_adv_states_completers_.emplace(completer.ToAsync());
242 MaybeUpdateLegacyAdvertisingStates();
243 }
244
handle_unknown_method(fidl::UnknownMethodMetadata<fhbt::Emulator> metadata,fidl::UnknownMethodCompleter::Sync & completer)245 void EmulatorDevice::handle_unknown_method(
246 fidl::UnknownMethodMetadata<fhbt::Emulator> metadata,
247 fidl::UnknownMethodCompleter::Sync& completer) {
248 FDF_LOG(
249 ERROR,
250 "Unknown method in Emulator request, closing with ZX_ERR_NOT_SUPPORTED");
251 completer.Close(ZX_ERR_NOT_SUPPORTED);
252 }
253
GetFeatures(GetFeaturesCompleter::Sync & completer)254 void EmulatorDevice::GetFeatures(GetFeaturesCompleter::Sync& completer) {
255 completer.Reply(fuchsia_hardware_bluetooth::wire::VendorFeatures());
256 }
257
EncodeCommand(EncodeCommandRequestView request,EncodeCommandCompleter::Sync & completer)258 void EmulatorDevice::EncodeCommand(EncodeCommandRequestView request,
259 EncodeCommandCompleter::Sync& completer) {
260 completer.ReplyError(ZX_ERR_INVALID_ARGS);
261 }
262
OpenHciTransport(OpenHciTransportCompleter::Sync & completer)263 void EmulatorDevice::OpenHciTransport(
264 OpenHciTransportCompleter::Sync& completer) {
265 // Sets FakeController to respond with event, ACL, and ISO packet types when
266 // it receives an incoming packet of the appropriate type
267 fake_device_.SetEventFunction(
268 fit::bind_member<&EmulatorDevice::SendEventToHost>(this));
269 fake_device_.SetReceiveAclFunction(
270 fit::bind_member<&EmulatorDevice::SendAclPacketToHost>(this));
271 fake_device_.SetReceiveIsoFunction(
272 fit::bind_member<&EmulatorDevice::SendIsoPacketToHost>(this));
273
274 auto endpoints = fidl::CreateEndpoints<fhbt::HciTransport>();
275 if (endpoints.is_error()) {
276 FDF_LOG(ERROR,
277 "Failed to create endpoints: %s",
278 zx_status_get_string(endpoints.error_value()));
279 completer.ReplyError(endpoints.error_value());
280 return;
281 }
282
283 hci_transport_bindings_.AddBinding(
284 fdf::Dispatcher::GetCurrent()->async_dispatcher(),
285 std::move(endpoints->server),
286 this,
287 fidl::kIgnoreBindingClosure);
288 completer.ReplySuccess(std::move(endpoints->client));
289 }
290
OpenSnoop(OpenSnoopCompleter::Sync & completer)291 void EmulatorDevice::OpenSnoop(OpenSnoopCompleter::Sync& completer) {
292 completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
293 }
294
handle_unknown_method(fidl::UnknownMethodMetadata<fhbt::Vendor> metadata,fidl::UnknownMethodCompleter::Sync & completer)295 void EmulatorDevice::handle_unknown_method(
296 fidl::UnknownMethodMetadata<fhbt::Vendor> metadata,
297 fidl::UnknownMethodCompleter::Sync& completer) {
298 FDF_LOG(
299 ERROR,
300 "Unknown method in Vendor request, closing with ZX_ERR_NOT_SUPPORTED");
301 completer.Close(ZX_ERR_NOT_SUPPORTED);
302 }
303
Send(SendRequest & request,SendCompleter::Sync & completer)304 void EmulatorDevice::Send(SendRequest& request,
305 SendCompleter::Sync& completer) {
306 switch (request.Which()) {
307 case fhbt::SentPacket::Tag::kCommand: {
308 std::vector<uint8_t> command_data = request.command().value();
309 pw::span<const std::byte> buffer = bt::BufferView(command_data).subspan();
310 fake_device_.SendCommand(buffer);
311 completer.Reply();
312 return;
313 }
314 case fhbt::SentPacket::Tag::kAcl: {
315 std::vector<uint8_t> acl_data = request.acl().value();
316 pw::span<const std::byte> buffer = bt::BufferView(acl_data).subspan();
317 fake_device_.SendAclData(buffer);
318 completer.Reply();
319 return;
320 }
321 case fhbt::SentPacket::Tag::kIso: {
322 std::vector<uint8_t> iso_data = request.iso().value();
323 pw::span<const std::byte> buffer = bt::BufferView(iso_data).subspan();
324 fake_device_.SendIsoData(buffer);
325 completer.Reply();
326 return;
327 }
328 default: {
329 FDF_LOG(ERROR,
330 "Received unknown packet type %lu",
331 static_cast<uint64_t>(request.Which()));
332 return;
333 }
334 }
335 }
336
ConfigureSco(ConfigureScoRequest & request,fidl::Server<fuchsia_hardware_bluetooth::HciTransport>::ConfigureScoCompleter::Sync & completer)337 void EmulatorDevice::ConfigureSco(
338 ConfigureScoRequest& request,
339 fidl::Server<fuchsia_hardware_bluetooth::HciTransport>::
340 ConfigureScoCompleter::Sync& completer) {
341 // This interface is not implemented.
342 completer.Close(ZX_ERR_NOT_SUPPORTED);
343 }
344
handle_unknown_method(::fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::HciTransport> metadata,::fidl::UnknownMethodCompleter::Sync & completer)345 void EmulatorDevice::handle_unknown_method(
346 ::fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::HciTransport>
347 metadata,
348 ::fidl::UnknownMethodCompleter::Sync& completer) {
349 FDF_LOG(ERROR,
350 "Unknown method in HciTransport request, closing with "
351 "ZX_ERR_NOT_SUPPORTED");
352 completer.Close(ZX_ERR_NOT_SUPPORTED);
353 }
354
ConnectEmulator(fidl::ServerEnd<fhbt::Emulator> request)355 void EmulatorDevice::ConnectEmulator(fidl::ServerEnd<fhbt::Emulator> request) {
356 emulator_binding_group_.AddBinding(
357 fdf::Dispatcher::GetCurrent()->async_dispatcher(),
358 std::move(request),
359 this,
360 fidl::kIgnoreBindingClosure);
361 }
362
ConnectVendor(fidl::ServerEnd<fhbt::Vendor> request)363 void EmulatorDevice::ConnectVendor(fidl::ServerEnd<fhbt::Vendor> request) {
364 vendor_binding_group_.AddBinding(
365 fdf::Dispatcher::GetCurrent()->async_dispatcher(),
366 std::move(request),
367 this,
368 fidl::kIgnoreBindingClosure);
369 }
370
AddHciDeviceChildNode()371 zx_status_t EmulatorDevice::AddHciDeviceChildNode() {
372 // Create args to add bt-hci-device as a child node on behalf of
373 // VirtualController
374 zx::result connector = vendor_devfs_connector_.Bind(
375 fdf::Dispatcher::GetCurrent()->async_dispatcher());
376 if (connector.is_error()) {
377 FDF_LOG(ERROR,
378 "Failed to bind devfs connecter to dispatcher: %u",
379 connector.status_value());
380 return connector.error_value();
381 }
382
383 fidl::Arena args_arena;
384 auto devfs =
385 fuchsia_driver_framework::wire::DevfsAddArgs::Builder(args_arena)
386 .connector(std::move(connector.value()))
387 .connector_supports(fuchsia_device_fs::ConnectionType::kController)
388 .class_name("bt-hci")
389 .Build();
390 auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(args_arena)
391 .name("bt-hci-device")
392 .devfs_args(devfs)
393 .Build();
394
395 // Create the endpoints of fuchsia_driver_framework::NodeController protocol
396 auto controller_endpoints =
397 fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
398 if (controller_endpoints.is_error()) {
399 FDF_LOG(ERROR,
400 "Create node controller endpoints failed: %s",
401 zx_status_get_string(controller_endpoints.error_value()));
402 return controller_endpoints.error_value();
403 }
404
405 // Create the endpoints of fuchsia_driver_framework::Node protocol for the
406 // child node, and hold the client end of it, because no driver will bind to
407 // the child node.
408 auto child_node_endpoints =
409 fidl::CreateEndpoints<fuchsia_driver_framework::Node>();
410 if (child_node_endpoints.is_error()) {
411 FDF_LOG(ERROR,
412 "Create child node endpoints failed: %s",
413 zx_status_get_string(child_node_endpoints.error_value()));
414 return child_node_endpoints.error_value();
415 }
416
417 // Add bt-hci-device as a child node of the EmulatorDevice
418 ZX_DEBUG_ASSERT(emulator_child_node()->is_valid());
419 auto child_result = emulator_child_node()->sync()->AddChild(
420 std::move(args),
421 std::move(controller_endpoints->server),
422 std::move(child_node_endpoints->server));
423 if (!child_result.ok()) {
424 FDF_LOG(ERROR,
425 "Failed to add bt-hci-device node, FIDL error: %s",
426 child_result.status_string());
427 return child_result.status();
428 }
429
430 if (child_result->is_error()) {
431 FDF_LOG(ERROR,
432 "Failed to add bt-hci-device node: %u",
433 static_cast<uint32_t>(child_result->error_value()));
434 return ZX_ERR_INTERNAL;
435 }
436
437 // |hci_child_node_| does not need to create more child nodes so we do not
438 // need an event_handler and we do not need to worry about it being re-bound
439 hci_child_node_.Bind(std::move(child_node_endpoints->client),
440 fdf::Dispatcher::GetCurrent()->async_dispatcher());
441 hci_node_controller_.Bind(std::move(controller_endpoints->client),
442 fdf::Dispatcher::GetCurrent()->async_dispatcher());
443
444 return ZX_OK;
445 }
446
AddPeer(std::unique_ptr<EmulatedPeer> peer)447 void EmulatorDevice::AddPeer(std::unique_ptr<EmulatedPeer> peer) {
448 auto address = peer->address();
449 peer->set_closed_callback([this, address] { peers_.erase(address); });
450 peers_[address] = std::move(peer);
451 }
452
OnControllerParametersChanged()453 void EmulatorDevice::OnControllerParametersChanged() {
454 fhbt::ControllerParameters fidl_value;
455 fidl_value.local_name(fake_device_.local_name());
456
457 const auto& device_class_bytes = fake_device_.device_class().bytes();
458 uint32_t device_class = 0;
459 device_class |= device_class_bytes[0];
460 device_class |= static_cast<uint32_t>(device_class_bytes[1]) << 8;
461 device_class |= static_cast<uint32_t>(device_class_bytes[2]) << 16;
462
463 std::optional<fuchsia_bluetooth::DeviceClass> device_class_option =
464 fuchsia_bluetooth::DeviceClass{device_class};
465 fidl_value.device_class(device_class_option);
466
467 controller_parameters_.emplace(fidl_value);
468 MaybeUpdateControllerParametersChanged();
469 }
470
MaybeUpdateControllerParametersChanged()471 void EmulatorDevice::MaybeUpdateControllerParametersChanged() {
472 if (!controller_parameters_.has_value() ||
473 !controller_parameters_completer_.has_value()) {
474 return;
475 }
476 controller_parameters_completer_->Reply(
477 std::move(controller_parameters_.value()));
478 controller_parameters_.reset();
479 controller_parameters_completer_.reset();
480 }
481
OnLegacyAdvertisingStateChanged()482 void EmulatorDevice::OnLegacyAdvertisingStateChanged() {
483 // We have requests to resolve. Construct the FIDL table for the current
484 // state.
485 fhbt::LegacyAdvertisingState fidl_state;
486 const FakeController::LEAdvertisingState& adv_state =
487 fake_device_.legacy_advertising_state();
488 fidl_state.enabled(adv_state.enabled);
489
490 // Populate the rest only if advertising is enabled.
491 fidl_state.type(static_cast<fhbt::LegacyAdvertisingType>(
492 bt::hci::LowEnergyAdvertiser::
493 AdvertisingEventPropertiesToLEAdvertisingType(adv_state.properties)));
494 fidl_state.address_type(LeOwnAddressTypeToFidl(adv_state.own_address_type));
495
496 if (adv_state.interval_min) {
497 fidl_state.interval_min(adv_state.interval_min);
498 }
499 if (adv_state.interval_max) {
500 fidl_state.interval_max(adv_state.interval_max);
501 }
502
503 if (adv_state.data_length) {
504 std::vector<uint8_t> output(adv_state.data,
505 adv_state.data + adv_state.data_length);
506
507 if (!fidl_state.advertising_data().has_value()) {
508 fidl_state.advertising_data() = fhbt::AdvertisingData();
509 }
510 fidl_state.advertising_data()->data(output);
511 }
512 if (adv_state.scan_rsp_length) {
513 std::vector<uint8_t> output(
514 adv_state.scan_rsp_data,
515 adv_state.scan_rsp_data + adv_state.scan_rsp_length);
516 if (!fidl_state.scan_response().has_value()) {
517 fidl_state.scan_response() = fhbt::AdvertisingData();
518 }
519 fidl_state.scan_response()->data(output);
520 }
521
522 legacy_adv_states_.emplace_back(fidl_state);
523 MaybeUpdateLegacyAdvertisingStates();
524 }
525
MaybeUpdateLegacyAdvertisingStates()526 void EmulatorDevice::MaybeUpdateLegacyAdvertisingStates() {
527 if (legacy_adv_states_.empty() || legacy_adv_states_completers_.empty()) {
528 return;
529 }
530 while (!legacy_adv_states_completers_.empty()) {
531 legacy_adv_states_completers_.front().Reply(legacy_adv_states_);
532 legacy_adv_states_completers_.pop();
533 }
534 legacy_adv_states_.clear();
535 }
536
OnPeerConnectionStateChanged(const DeviceAddress & address,bt::hci_spec::ConnectionHandle handle,bool connected,bool canceled)537 void EmulatorDevice::OnPeerConnectionStateChanged(
538 const DeviceAddress& address,
539 bt::hci_spec::ConnectionHandle handle,
540 bool connected,
541 bool canceled) {
542 FDF_LOG(TRACE,
543 "Peer connection state changed: %s (handle: %#.4x) (connected: %s) "
544 "(canceled: %s):\n",
545 address.ToString().c_str(),
546 handle,
547 (connected ? "true" : "false"),
548 (canceled ? "true" : "false"));
549
550 auto iter = peers_.find(address);
551 if (iter != peers_.end()) {
552 iter->second->UpdateConnectionState(connected);
553 }
554 }
555
UnpublishHci()556 void EmulatorDevice::UnpublishHci() {
557 // Unpublishing the bt-hci-device child node shuts down the associated bt-host
558 // component
559 auto status = hci_node_controller_->Remove();
560 if (!status.ok()) {
561 FDF_LOG(ERROR,
562 "Could not remove bt-hci-device child node: %s",
563 status.status_string());
564 }
565 }
566
SendEventToHost(pw::span<const std::byte> buffer)567 void EmulatorDevice::SendEventToHost(pw::span<const std::byte> buffer) {
568 if (hci_transport_bindings_.size() == 0) {
569 FDF_LOG(ERROR, "No HciTransport bindings");
570 return;
571 }
572 // Using HciTransport protocol (i.e. |cmd_channel_| is not set)
573 hci_transport_bindings_.ForEachBinding(
574 [buffer](
575 const fidl::ServerBinding<fuchsia_hardware_bluetooth::HciTransport>&
576 binding) {
577 // Send the event to bt-host
578 std::vector<uint8_t> data = bt::BufferView(buffer).ToVector();
579 fhbt::ReceivedPacket event = fhbt::ReceivedPacket::WithEvent(data);
580 fit::result<::fidl::OneWayError> status =
581 fidl::SendEvent(binding)->OnReceive(event);
582 if (!status.is_ok()) {
583 FDF_LOG(ERROR,
584 "Failed to send OnReceive event to bt-host: %s",
585 status.error_value().status_string());
586 }
587 });
588 }
589
SendAclPacketToHost(pw::span<const std::byte> buffer)590 void EmulatorDevice::SendAclPacketToHost(pw::span<const std::byte> buffer) {
591 if (hci_transport_bindings_.size() == 0) {
592 FDF_LOG(ERROR, "No HciTransport bindings");
593 return;
594 }
595 // Using HciTransport protocol (i.e. |acl_channel_| is not set)
596 hci_transport_bindings_.ForEachBinding(
597 [buffer](
598 const fidl::ServerBinding<fuchsia_hardware_bluetooth::HciTransport>&
599 binding) {
600 // Send the event to bt-host
601 std::vector<uint8_t> data = bt::BufferView(buffer).ToVector();
602 fhbt::ReceivedPacket event = fhbt::ReceivedPacket::WithAcl(data);
603 fit::result<::fidl::OneWayError> status =
604 fidl::SendEvent(binding)->OnReceive(event);
605 if (!status.is_ok()) {
606 FDF_LOG(ERROR,
607 "Failed to send OnReceive event to bt-host: %s",
608 status.error_value().status_string());
609 }
610 });
611 }
612
SendIsoPacketToHost(pw::span<const std::byte> buffer)613 void EmulatorDevice::SendIsoPacketToHost(pw::span<const std::byte> buffer) {
614 if (hci_transport_bindings_.size() == 0) {
615 FDF_LOG(ERROR, "No HciTransport bindings");
616 return;
617 }
618 // Using HciTransport protocol (i.e. |iso_channel_| is not set)
619 hci_transport_bindings_.ForEachBinding(
620 [buffer](
621 const fidl::ServerBinding<fuchsia_hardware_bluetooth::HciTransport>&
622 binding) {
623 // Send the event to bt-host
624 std::vector<uint8_t> data = bt::BufferView(buffer).ToVector();
625 fhbt::ReceivedPacket event = fhbt::ReceivedPacket::WithIso(data);
626 fit::result<::fidl::OneWayError> status =
627 fidl::SendEvent(binding)->OnReceive(event);
628 if (!status.is_ok()) {
629 FDF_LOG(ERROR,
630 "Failed to send OnReceive event to bt-host: %s",
631 status.error_value().status_string());
632 }
633 });
634 }
635
636 } // namespace bt_hci_virtual
637