blob: 2d3f0ca542e85d052be51b2fcf2d38bf3f5277f1 [file] [log] [blame]
// Copyright 2020 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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_interrogator.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/peer.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
namespace bt::gap {
LowEnergyInterrogator::LowEnergyInterrogator(
Peer::WeakPtr peer,
hci_spec::ConnectionHandle handle,
hci::CommandChannel::WeakPtr cmd_channel,
bool sca_supported)
: peer_(std::move(peer)),
peer_id_(peer_->identifier()),
handle_(handle),
cmd_runner_(cmd_channel->AsWeakPtr()),
controller_supports_sca_(sca_supported),
weak_self_(this) {}
void LowEnergyInterrogator::Start(ResultCallback callback) {
BT_ASSERT(!callback_);
callback_ = std::move(callback);
if (!peer_.is_alive()) {
Complete(ToResult(HostError::kFailed));
return;
}
BT_ASSERT(peer_->le().has_value());
// Always read remote version information as a test of whether the connection
// was *actually* successfully established. If the connection failed to be
// established, the command status of the Read Remote Version Information
// command will be "Connection Failed to be Established".
// See https://fxbug.dev/42138706 for details.
QueueReadRemoteVersionInformation();
if (!peer_->le()->features().has_value()) {
QueueReadLERemoteFeatures();
}
cmd_runner_.RunCommands([this](hci::Result<> result) {
if (result.is_error() || !peer_->le()->features().has_value() ||
!controller_supports_sca_) {
Complete(result);
return;
}
// Verify the peer supports SCA updates
if (!(peer_->le()->features()->le_features &
static_cast<uint64_t>(
hci_spec::LESupportedFeature::kSleepClockAccuracyUpdates))) {
bt_log(INFO, "gap-le", "peer %s does not support SCA", bt_str(peer_id_));
Complete(result);
return;
}
QueueRequestPeerSca();
});
}
void LowEnergyInterrogator::Cancel() {
if (!cmd_runner_.IsReady()) {
cmd_runner_.Cancel();
}
}
void LowEnergyInterrogator::Complete(hci::Result<> result) {
if (!callback_) {
return;
}
auto self = weak_self_.GetWeakPtr();
// callback may destroy this object
callback_(result);
// Complete() may have been called by a command callback, in which case the
// runner needs to be canceled.
if (self.is_alive() && !cmd_runner_.IsReady()) {
cmd_runner_.Cancel();
}
}
void LowEnergyInterrogator::QueueRequestPeerSca() {
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::LERequestPeerSCACommandWriter>(
hci_spec::kLERequestPeerSCA);
packet.view_t().connection_handle().Write(handle_);
// It's safe to capture |this| instead of a weak ptr to self because
// |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
// destroyed, and |this| outlives |cmd_runner_|.
auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
if (hci_is_error(event, WARN, "gap-le", "LE request peer SCA failed")) {
return;
}
auto view = event.view<
pw::bluetooth::emboss::LERequestPeerSCACompleteSubeventView>();
bt_log(DEBUG,
"gap-le",
"LE request peer SCA complete (peer: %s, value: %s)",
bt_str(peer_id_),
TryToGetNameFromEnum(view.peer_clock_accuracy().Read()));
peer_->MutLe().set_sleep_clock_accuracy(view.peer_clock_accuracy().Read());
};
bt_log(TRACE, "gap-le", "requesting SCA for peer %s", bt_str(peer_id_));
cmd_runner_.QueueLeAsyncCommand(
std::move(packet),
hci_spec::kLERequestPeerSCACompleteSubeventCode,
std::move(cmd_cb),
/*wait=*/true);
cmd_runner_.RunCommands([this](hci::Result<> result) {
// This shouldn't happen since we verified that the peer supports SCA
// updates
BT_DEBUG_ASSERT_MSG(!result.is_error(),
"request for SCA from peer %s failed",
bt_str(peer_id_));
// Report success since the data is not critical and we don't want to
// interrupt pairing
Complete(fit::ok());
});
}
void LowEnergyInterrogator::QueueReadLERemoteFeatures() {
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::LEReadRemoteFeaturesCommandWriter>(
hci_spec::kLEReadRemoteFeatures);
packet.view_t().connection_handle().Write(handle_);
// It's safe to capture |this| instead of a weak ptr to self because
// |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
// destroyed, and |this| outlives |cmd_runner_|.
auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
if (hci_is_error(event, WARN, "gap-le", "LE read remote features failed")) {
return;
}
bt_log(DEBUG,
"gap-le",
"LE read remote features complete (peer: %s)",
bt_str(peer_id_));
auto view = event.view<
pw::bluetooth::emboss::LEReadRemoteFeaturesCompleteSubeventView>();
peer_->MutLe().SetFeatures(hci_spec::LESupportedFeatures{
view.le_features().BackingStorage().ReadUInt()});
};
bt_log(TRACE,
"gap-le",
"sending LE read remote features command (peer id: %s)",
bt_str(peer_id_));
cmd_runner_.QueueLeAsyncCommand(
std::move(packet),
hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
std::move(cmd_cb),
/*wait=*/false);
}
void LowEnergyInterrogator::QueueReadRemoteVersionInformation() {
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::ReadRemoteVersionInfoCommandWriter>(
hci_spec::kReadRemoteVersionInfo);
packet.view_t().connection_handle().Write(handle_);
// It's safe to capture |this| instead of a weak ptr to self because
// |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
// destroyed, and |this| outlives |cmd_runner_|.
auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
if (hci_is_error(
event, WARN, "gap-le", "read remote version info failed")) {
return;
}
BT_DEBUG_ASSERT(event.event_code() ==
hci_spec::kReadRemoteVersionInfoCompleteEventCode);
bt_log(TRACE,
"gap-le",
"read remote version info completed (peer: %s)",
bt_str(peer_id_));
auto view = event.view<
pw::bluetooth::emboss::ReadRemoteVersionInfoCompleteEventView>();
peer_->set_version(view.version().Read(),
view.company_identifier().Read(),
view.subversion().Read());
};
bt_log(TRACE,
"gap-le",
"asking for version info (peer id: %s)",
bt_str(peer_id_));
cmd_runner_.QueueCommand(std::move(packet),
std::move(cmd_cb),
/*wait=*/false,
hci_spec::kReadRemoteVersionInfoCompleteEventCode);
}
} // namespace bt::gap