blob: 2583fe7ed29ffab753f9b57276b1e9de37605a41 [file] [log] [blame]
// Copyright 2017 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 "transport.h"
#include <lib/async/default.h>
#include <lib/zx/channel.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include "device_wrapper.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
namespace bt::hci {
fit::result<std::unique_ptr<Transport>> Transport::Create(
std::unique_ptr<DeviceWrapper> hci_device) {
auto transport = std::unique_ptr<Transport>(new Transport(std::move(hci_device)));
if (!transport->command_channel()) {
return fit::error();
}
return fit::ok(std::move(transport));
}
Transport::Transport(std::unique_ptr<DeviceWrapper> hci_device)
: hci_device_(std::move(hci_device)), weak_ptr_factory_(this) {
ZX_ASSERT(hci_device_);
bt_log(INFO, "hci", "initializing HCI");
// Obtain command channel handle.
zx::channel channel = hci_device_->GetCommandChannel();
if (!channel.is_valid()) {
bt_log(ERROR, "hci", "failed to obtain command channel handle");
return;
}
// We watch for handle errors and closures to perform the necessary clean up.
WatchChannelClosed(channel, cmd_channel_wait_);
auto command_channel_result = CommandChannel::Create(this, std::move(channel));
if (command_channel_result.is_error()) {
return;
}
command_channel_ = command_channel_result.take_value();
command_channel_->set_channel_timeout_cb(fit::bind_member(this, &Transport::OnChannelError));
}
Transport::~Transport() {
ZX_ASSERT(thread_checker_.is_thread_valid());
bt_log(INFO, "hci", "transport shutting down");
if (acl_data_channel_) {
acl_data_channel_->ShutDown();
}
if (command_channel_) {
command_channel_->ShutDown();
}
cmd_channel_wait_.Cancel();
if (acl_data_channel_) {
acl_channel_wait_.Cancel();
}
bt_log(INFO, "hci", "transport shut down complete");
}
bool Transport::InitializeACLDataChannel(const DataBufferInfo& bredr_buffer_info,
const DataBufferInfo& le_buffer_info) {
// Obtain ACL data channel handle.
zx::channel channel = hci_device_->GetACLDataChannel();
if (!channel.is_valid()) {
bt_log(ERROR, "hci", "failed to obtain ACL data channel handle");
return false;
}
// We watch for handle errors and closures to perform the necessary clean up.
WatchChannelClosed(channel, acl_channel_wait_);
acl_data_channel_ = AclDataChannel::Create(this, std::move(channel));
acl_data_channel_->Initialize(bredr_buffer_info, le_buffer_info);
return true;
}
bt_vendor_features_t Transport::GetVendorFeatures() { return hci_device_->GetVendorFeatures(); }
fit::result<DynamicByteBuffer> Transport::EncodeVendorCommand(bt_vendor_command_t command,
bt_vendor_params_t& params) {
return hci_device_->EncodeVendorCommand(command, params);
}
void Transport::SetTransportClosedCallback(fit::closure callback) {
ZX_ASSERT(callback);
ZX_ASSERT(!closed_cb_);
closed_cb_ = std::move(callback);
}
void Transport::WatchChannelClosed(const zx::channel& channel, Waiter& wait) {
wait.set_object(channel.get());
wait.set_trigger(ZX_CHANNEL_PEER_CLOSED);
zx_status_t status = wait.Begin(async_get_default_dispatcher());
if (status != ZX_OK) {
bt_log(ERROR, "hci", "failed to set up closed handler: %s", zx_status_get_string(status));
wait.set_object(ZX_HANDLE_INVALID);
}
}
void Transport::OnChannelClosed(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
bt_log(ERROR, "hci", "channel error: %s", zx_status_get_string(status));
} else {
ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED);
}
NotifyClosedCallback();
}
void Transport::NotifyClosedCallback() {
// Clear the handlers so that we stop receiving events.
cmd_channel_wait_.Cancel();
if (acl_data_channel_) {
acl_channel_wait_.Cancel();
}
bt_log(INFO, "hci", "channel(s) were closed");
if (closed_cb_) {
closed_cb_();
}
}
void Transport::OnChannelError() {
bt_log(ERROR, "hci", "channel error");
// TODO(fxbug.dev/52588): remove calls to ShutDown() here as Host will destroy Transport in closed
// callback
if (acl_data_channel_) {
acl_data_channel_->ShutDown();
}
if (command_channel_) {
command_channel_->ShutDown();
}
NotifyClosedCallback();
}
} // namespace bt::hci