blob: 28de9e933e6a0af43dd1e8c27eedaf7431140a0c [file] [log] [blame]
// 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_device.h"
#include <zircon/status.h>
#include "host.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/device_wrapper.h"
namespace bthost {
HostDevice::HostDevice(zx_device_t* device)
: dev_(nullptr), parent_(device), loop_(&kAsyncLoopConfigNoAttachToThread) {
ZX_DEBUG_ASSERT(parent_);
dev_proto_.version = DEVICE_OPS_VERSION;
dev_proto_.unbind = &HostDevice::DdkUnbind;
dev_proto_.release = &HostDevice::DdkRelease;
dev_proto_.message = &HostDevice::DdkMessage;
}
zx_status_t HostDevice::Bind() {
bt_log(TRACE, "bt-host", "bind");
std::lock_guard<std::mutex> lock(mtx_);
bt_hci_protocol_t hci_proto;
zx_status_t status =
device_get_protocol(parent_, ZX_PROTOCOL_BT_HCI, &hci_proto);
if (status != ZX_OK) {
bt_log(ERROR, "bt-host", "failed to obtain bt-hci protocol ops: %s",
zx_status_get_string(status));
return status;
}
if (!hci_proto.ops) {
bt_log(ERROR, "bt-host", "bt-hci device ops required!");
return ZX_ERR_NOT_SUPPORTED;
}
if (!hci_proto.ops->open_command_channel) {
bt_log(ERROR, "bt-host", "bt-hci op required: open_command_channel");
return ZX_ERR_NOT_SUPPORTED;
}
if (!hci_proto.ops->open_acl_data_channel) {
bt_log(ERROR, "bt-host", "bt-hci op required: open_acl_data_channel");
return ZX_ERR_NOT_SUPPORTED;
}
if (!hci_proto.ops->open_snoop_channel) {
bt_log(ERROR, "bt-host", "bt-hci op required: open_snoop_channel");
return ZX_ERR_NOT_SUPPORTED;
}
// We are required to publish a device before returning from Bind but we
// haven't fully initialized the adapter yet. We create the bt-host device as
// invisible until initialization completes on the host thread. We also
// disallow other drivers from directly binding to it.
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "bt_host",
.ctx = this,
.ops = &dev_proto_,
.proto_id = ZX_PROTOCOL_BT_HOST,
.flags = DEVICE_ADD_NON_BINDABLE | DEVICE_ADD_INVISIBLE,
};
status = device_add(parent_, &args, &dev_);
if (status != ZX_OK) {
bt_log(ERROR, "bt-host", "Failed to publish device: %s",
zx_status_get_string(status));
return status;
}
loop_.StartThread("bt-host (gap)");
// Send the bootstrap message to Host. The Host object can only be accessed on
// the Host thread.
async::PostTask(loop_.dispatcher(), [hci_proto, this] {
bt_log(SPEW, "bt-host", "host thread start");
std::lock_guard<std::mutex> lock(mtx_);
host_ = fxl::MakeRefCounted<Host>(hci_proto);
host_->Initialize([host = host_, this](bool success) {
{
std::lock_guard<std::mutex> lock(mtx_);
// Abort if CleanUp has been called.
if (!host_)
return;
if (success) {
bt_log(TRACE, "bt-host", "adapter initialized; make device visible");
host_->gatt_host()->SetRemoteServiceWatcher(
fit::bind_member(this, &HostDevice::OnRemoteGattServiceAdded));
device_make_visible(dev_);
return;
}
bt_log(ERROR, "bt-host", "failed to initialize adapter");
CleanUp();
}
host->ShutDown();
loop_.Shutdown();
});
});
return ZX_OK;
}
void HostDevice::Unbind() {
bt_log(TRACE, "bt-host", "unbind");
std::lock_guard<std::mutex> lock(mtx_);
if (!host_)
return;
// Do this immediately to stop receiving new service callbacks.
host_->gatt_host()->SetRemoteServiceWatcher({});
async::PostTask(loop_.dispatcher(), [this, host = host_] {
host->ShutDown();
loop_.Quit();
});
// Make sure that the ShutDown task runs before this returns.
loop_.JoinThreads();
CleanUp();
}
void HostDevice::Release() {
bt_log(TRACE, "bt-host", "release");
delete this;
}
zx_status_t HostDevice::OpenHostChannel(zx::channel channel) {
ZX_DEBUG_ASSERT(channel);
std::lock_guard<std::mutex> lock(mtx_);
// It's possible that this is being processed whilst the device is unbinding,
// in which case |host_| can be null
if (!host_) {
bt_log(ERROR, "bt-host", "Cannot open channel, host is unbound");
return ZX_ERR_BAD_STATE;
}
// Tell Host to start processing messages on this handle.
ZX_DEBUG_ASSERT(host_);
async::PostTask(loop_.dispatcher(),
[host = host_, chan = std::move(channel)]() mutable {
host->BindHostInterface(std::move(chan));
});
return ZX_OK;
}
void HostDevice::OnRemoteGattServiceAdded(
bt::gatt::PeerId peer_id, fbl::RefPtr<bt::gatt::RemoteService> service) {
auto gatt_device =
std::make_unique<GattRemoteServiceDevice>(dev_, peer_id, service);
auto gatt_device_ptr = gatt_device.get();
zx_status_t status = gatt_device->Bind();
if (status != ZX_OK)
return;
gatt_devices_[gatt_device_ptr] = std::move(gatt_device);
service->AddRemovedHandler(
[this, gatt_device_ptr] { gatt_devices_.erase(gatt_device_ptr); });
}
void HostDevice::CleanUp() {
host_ = nullptr;
// Removing the devices explicitly instead of letting unbind handle it for us.
gatt_devices_.clear();
device_remove(dev_);
dev_ = nullptr;
}
} // namespace bthost