| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "host.h" |
| |
| #include "fidl/host_server.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/hci_wrapper.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/transport.h" |
| |
| using namespace bt; |
| |
| namespace bthost { |
| |
| Host::Host(const bt_hci_protocol_t& hci_proto, std::optional<bt_vendor_protocol_t> vendor_proto) |
| : hci_proto_(hci_proto), vendor_proto_(vendor_proto) {} |
| |
| Host::~Host() {} |
| |
| // static |
| fbl::RefPtr<Host> Host::Create(const bt_hci_protocol_t& hci_proto, |
| std::optional<bt_vendor_protocol_t> vendor_proto) { |
| return fbl::AdoptRef(new Host(hci_proto, vendor_proto)); |
| } |
| |
| bool Host::Initialize(inspect::Node& root_node, InitCallback init_cb, ErrorCallback error_cb) { |
| auto dev = std::make_unique<hci::DdkDeviceWrapper>(hci_proto_, vendor_proto_); |
| auto hci_wrapper = hci::HciWrapper::Create(std::move(dev), async_get_default_dispatcher()); |
| |
| auto hci_result = hci::Transport::Create(std::move(hci_wrapper)); |
| if (hci_result.is_error()) { |
| bt_log(ERROR, "bt-host", "failed to initialize HCI transport"); |
| return false; |
| } |
| hci_ = hci_result.take_value(); |
| |
| gatt_ = gatt::GATT::Create(); |
| |
| gap_ = gap::Adapter::Create(hci_->WeakPtr(), gatt_->AsWeakPtr()); |
| if (!gap_) |
| return false; |
| |
| gap_->AttachInspect(root_node, "adapter"); |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| |
| // Called when the GAP layer is ready. We initialize the GATT profile after |
| // initial setup in GAP. The data domain will be initialized by GAP because it |
| // both sets up the HCI ACL data channel that L2CAP relies on and registers |
| // L2CAP services. |
| auto gap_init_callback = [callback = std::move(init_cb)](bool success) mutable { |
| bt_log(DEBUG, "bt-host", "GAP init complete (%s)", (success ? "success" : "failure")); |
| |
| callback(success); |
| }; |
| |
| auto transport_closed_callback = [error_cb = std::move(error_cb)]() mutable { |
| bt_log(WARN, "bt-host", "bt-host: HCI transport has closed"); |
| error_cb(); |
| }; |
| |
| bt_log(DEBUG, "bt-host", "initializing GAP"); |
| return gap_->Initialize(std::move(gap_init_callback), std::move(transport_closed_callback)); |
| } |
| |
| void Host::ShutDown() { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| bt_log(DEBUG, "bt-host", "shutting down"); |
| |
| if (!gap_) { |
| bt_log(DEBUG, "bt-host", "already shut down"); |
| return; |
| } |
| |
| // Closes all FIDL channels owned by |host_server_|. |
| host_server_ = nullptr; |
| |
| // Make sure that |gap_| gets shut down and destroyed on its creation thread |
| // as it is not thread-safe. |
| gap_->ShutDown(); |
| gap_ = nullptr; |
| |
| // This shuts down the GATT profile and all of its clients. |
| gatt_ = nullptr; |
| |
| // Shuts down HCI command channel and ACL data channel. |
| hci_ = nullptr; |
| } |
| |
| void Host::BindHostInterface(zx::channel channel) { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| if (host_server_) { |
| bt_log(WARN, "bt-host", "Host interface channel already open!"); |
| return; |
| } |
| |
| ZX_DEBUG_ASSERT(gap_); |
| ZX_DEBUG_ASSERT(gatt_); |
| |
| host_server_ = |
| std::make_unique<HostServer>(std::move(channel), gap_->AsWeakPtr(), gatt_->AsWeakPtr()); |
| host_server_->set_error_handler([this](zx_status_t status) { |
| ZX_DEBUG_ASSERT(host_server_); |
| bt_log(DEBUG, "bt-host", "Host interface disconnected"); |
| host_server_ = nullptr; |
| }); |
| } |
| |
| } // namespace bthost |