blob: 84bd00760080d5cc7759a7a6e383fa83fdfa59ac [file] [log] [blame]
// Copyright 2019 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 "connection.h"
#include <endian.h>
#include "lib/async/default.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/defaults.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/util.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/command_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/error.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/transport.h"
#include "src/connectivity/bluetooth/lib/cpp-string/string_printf.h"
namespace bt::hci {
Connection::Connection(hci_spec::ConnectionHandle handle, const DeviceAddress& local_address,
const DeviceAddress& peer_address, const fxl::WeakPtr<Transport>& hci,
fit::callback<void()> on_disconnection_complete)
: handle_(handle),
local_address_(local_address),
peer_address_(peer_address),
conn_state_(State::kConnected),
hci_(hci),
weak_ptr_factory_(this) {
ZX_ASSERT(handle_);
ZX_ASSERT(hci_);
auto disconn_complete_handler = [self = weak_ptr_factory_.GetWeakPtr(), handle, hci = hci_,
on_disconnection_complete =
std::move(on_disconnection_complete)](auto& event) mutable {
return Connection::OnDisconnectionComplete(self, handle, event,
std::move(on_disconnection_complete));
};
hci_->command_channel()->AddEventHandler(hci_spec::kDisconnectionCompleteEventCode,
std::move(disconn_complete_handler));
}
Connection::~Connection() {
if (conn_state_ == Connection::State::kConnected) {
Disconnect(hci_spec::StatusCode::kRemoteUserTerminatedConnection);
}
}
std::string Connection::ToString() const {
return bt_lib_cpp_string::StringPrintf("[HCI connection (handle: %#.4x, address: %s)]", handle_,
bt_str(peer_address_));
}
CommandChannel::EventCallbackResult Connection::OnDisconnectionComplete(
fxl::WeakPtr<Connection> self, hci_spec::ConnectionHandle handle, const EventPacket& event,
fit::callback<void()> on_disconnection_complete) {
ZX_ASSERT(event.event_code() == hci_spec::kDisconnectionCompleteEventCode);
if (event.view().payload_size() != sizeof(hci_spec::DisconnectionCompleteEventParams)) {
bt_log(WARN, "hci", "malformed disconnection complete event");
return CommandChannel::EventCallbackResult::kContinue;
}
const auto& params = event.params<hci_spec::DisconnectionCompleteEventParams>();
const auto event_handle = le16toh(params.connection_handle);
// Silently ignore this event as it isn't meant for this connection.
if (event_handle != handle) {
return CommandChannel::EventCallbackResult::kContinue;
}
bt_log(INFO, "hci", "disconnection complete - %s, handle: %#.4x, reason: %#.2x (%s)",
bt_str(event.ToResult()), handle, params.reason,
hci_spec::StatusCodeToString(params.reason).c_str());
if (self) {
self->conn_state_ = State::kDisconnected;
}
// Notify subclasses before notifying clients so that subclasses have a chance to use |this|.
if (on_disconnection_complete) {
on_disconnection_complete();
}
// Peer disconnect. Callback may destroy connection.
if (self && self->peer_disconnect_callback_) {
self->peer_disconnect_callback_(self.get(), params.reason);
}
return CommandChannel::EventCallbackResult::kRemove;
}
void Connection::Disconnect(hci_spec::StatusCode reason) {
ZX_ASSERT(conn_state_ == Connection::State::kConnected);
conn_state_ = Connection::State::kWaitingForDisconnectionComplete;
// Here we send a HCI_Disconnect command without waiting for it to complete.
auto status_cb = [](auto id, const EventPacket& event) {
ZX_DEBUG_ASSERT(event.event_code() == hci_spec::kCommandStatusEventCode);
hci_is_error(event, TRACE, "hci", "ignoring disconnection failure");
};
auto disconn =
CommandPacket::New(hci_spec::kDisconnect, sizeof(hci_spec::DisconnectCommandParams));
auto params = disconn->mutable_payload<hci_spec::DisconnectCommandParams>();
params->connection_handle = htole16(handle());
params->reason = reason;
bt_log(DEBUG, "hci", "disconnecting connection (handle: %#.4x, reason: %#.2x)", handle(), reason);
// Send HCI Disconnect.
hci_->command_channel()->SendCommand(std::move(disconn), std::move(status_cb),
hci_spec::kCommandStatusEventCode);
}
} // namespace bt::hci