blob: e54908108354d486151ee62ccd756ebfbd294584 [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/bredr_interrogator.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.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/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
namespace bt::gap {
BrEdrInterrogator::BrEdrInterrogator(Peer::WeakPtr peer,
hci_spec::ConnectionHandle handle,
hci::CommandChannel::WeakPtr cmd_channel)
: peer_(std::move(peer)),
peer_id_(peer_->identifier()),
handle_(handle),
cmd_runner_(std::move(cmd_channel)),
weak_self_(this) {
BT_ASSERT(peer_.is_alive());
}
void BrEdrInterrogator::Start(ResultCallback callback) {
callback_ = std::move(callback);
if (!peer_.is_alive() || !peer_->bredr()) {
Complete(ToResult(HostError::kFailed));
return;
}
if (!peer_->name()) {
QueueRemoteNameRequest();
}
if (!peer_->version()) {
QueueReadRemoteVersionInformation();
}
if (!peer_->features().HasPage(0)) {
QueueReadRemoteFeatures();
} else if (peer_->features().HasBit(
/*page=*/0, hci_spec::LMPFeature::kExtendedFeatures)) {
QueueReadRemoteExtendedFeatures(/*page=*/1);
}
if (!cmd_runner_.HasQueuedCommands()) {
Complete(fit::ok());
return;
}
cmd_runner_.RunCommands([this](hci::Result<> result) { Complete(result); });
}
void BrEdrInterrogator::Cancel() {
if (!cmd_runner_.IsReady()) {
cmd_runner_.Cancel();
}
}
void BrEdrInterrogator::Complete(hci::Result<> result) {
if (!callback_) {
return;
}
auto self = weak_self_.GetWeakPtr();
// callback may destroy this object
callback_(result);
if (self.is_alive() && !cmd_runner_.IsReady()) {
cmd_runner_.Cancel();
}
}
void BrEdrInterrogator::QueueRemoteNameRequest() {
pw::bluetooth::emboss::PageScanRepetitionMode mode =
pw::bluetooth::emboss::PageScanRepetitionMode::R0_;
if (peer_->bredr()->page_scan_repetition_mode()) {
mode = *peer_->bredr()->page_scan_repetition_mode();
}
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::RemoteNameRequestCommandWriter>(
hci_spec::kRemoteNameRequest);
auto params = packet.view_t();
params.bd_addr().CopyFrom(peer_->address().value().view());
params.page_scan_repetition_mode().Write(mode);
if (peer_->bredr()->clock_offset()) {
params.clock_offset().valid().Write(true);
const uint16_t offset = peer_->bredr()->clock_offset().value();
params.clock_offset().clock_offset().Write(offset);
}
auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
if (hci_is_error(event, WARN, "gap-bredr", "remote name request failed")) {
return;
}
bt_log(TRACE,
"gap-bredr",
"name request complete (peer id: %s)",
bt_str(peer_id_));
auto params =
event.view<pw::bluetooth::emboss::RemoteNameRequestCompleteEventView>();
emboss::support::ReadOnlyContiguousBuffer name =
params.remote_name().BackingStorage();
const unsigned char* name_end = std::find(name.begin(), name.end(), '\0');
std::string name_string(reinterpret_cast<const char*>(name.begin()),
reinterpret_cast<const char*>(name_end));
peer_->RegisterName(std::move(name_string),
Peer::NameSource::kNameDiscoveryProcedure);
};
bt_log(TRACE,
"gap-bredr",
"sending name request (peer id: %s)",
bt_str(peer_->identifier()));
cmd_runner_.QueueCommand(std::move(packet),
std::move(cmd_cb),
/*wait=*/false,
hci_spec::kRemoteNameRequestCompleteEventCode,
{hci_spec::kInquiry});
}
void BrEdrInterrogator::QueueReadRemoteFeatures() {
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::ReadRemoteSupportedFeaturesCommandWriter>(
hci_spec::kReadRemoteSupportedFeatures);
packet.view_t().connection_handle().Write(handle_);
auto cmd_cb = [this](const hci::EventPacket& event) {
if (hci_is_error(event,
WARN,
"gap-bredr",
"read remote supported features failed")) {
return;
}
bt_log(TRACE,
"gap-bredr",
"remote features request complete (peer id: %s)",
bt_str(peer_id_));
const auto& params =
event.view()
.payload<
hci_spec::ReadRemoteSupportedFeaturesCompleteEventParams>();
peer_->SetFeaturePage(0, le64toh(params.lmp_features));
if (peer_->features().HasBit(/*page=*/0,
hci_spec::LMPFeature::kExtendedFeatures)) {
peer_->set_last_page_number(1);
QueueReadRemoteExtendedFeatures(/*page=*/1);
}
};
bt_log(TRACE,
"gap-bredr",
"asking for supported features (peer id: %s)",
bt_str(peer_id_));
cmd_runner_.QueueCommand(
std::move(packet),
std::move(cmd_cb),
/*wait=*/false,
hci_spec::kReadRemoteSupportedFeaturesCompleteEventCode);
}
void BrEdrInterrogator::QueueReadRemoteExtendedFeatures(uint8_t page) {
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::ReadRemoteExtendedFeaturesCommandWriter>(
hci_spec::kReadRemoteExtendedFeatures);
auto params = packet.view_t();
params.connection_handle().Write(handle_);
params.page_number().Write(page);
auto cmd_cb = [this, page](const hci::EmbossEventPacket& event) {
if (hci_is_error(event,
WARN,
"gap-bredr",
"read remote extended features failed (peer id: %s)",
bt_str(peer_id_))) {
return;
}
auto view = event.view<
pw::bluetooth::emboss::ReadRemoteExtendedFeaturesCompleteEventView>();
bt_log(TRACE,
"gap-bredr",
"got extended features page %u, max page %u (requested page: %u, "
"peer id: %s)",
view.page_number().Read(),
view.max_page_number().Read(),
page,
bt_str(peer_id_));
peer_->SetFeaturePage(view.page_number().Read(),
view.lmp_features().BackingStorage().ReadUInt());
if (view.page_number().Read() != page) {
bt_log(INFO,
"gap-bredr",
"requested page %u and got page %u, giving up (peer: %s)",
page,
view.page_number().Read(),
bt_str(peer_id_));
peer_->set_last_page_number(0);
return;
}
// NOTE: last page number will be capped at 2
peer_->set_last_page_number(view.max_page_number().Read());
if (page < peer_->features().last_page_number()) {
QueueReadRemoteExtendedFeatures(page + 1);
}
};
bt_log(TRACE,
"gap-bredr",
"requesting extended features page %u (peer id: %s)",
page,
bt_str(peer_id_));
cmd_runner_.QueueCommand(
std::move(packet),
std::move(cmd_cb),
/*wait=*/false,
hci_spec::kReadRemoteExtendedFeaturesCompleteEventCode);
}
void BrEdrInterrogator::QueueReadRemoteVersionInformation() {
auto packet = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::ReadRemoteVersionInfoCommandWriter>(
hci_spec::kReadRemoteVersionInfo);
packet.view_t().connection_handle().Write(handle_);
auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
if (hci_is_error(event, WARN, "gap", "read remote version info failed")) {
return;
}
BT_DEBUG_ASSERT(event.event_code() ==
hci_spec::kReadRemoteVersionInfoCompleteEventCode);
bt_log(TRACE,
"gap",
"read remote version info completed (peer id: %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", "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