blob: 458d6e56df79071f2c509b87053f2dcc79894990 [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 "bredr_interrogator.h"
#include <zircon/assert.h>
#include "src/connectivity/bluetooth/core/bt-host/gap/peer.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/transport.h"
namespace bt {
namespace gap {
BrEdrInterrogator::Interrogation::Interrogation(hci::ConnectionPtr conn,
ResultCallback cb)
: conn_ptr(std::move(conn)), result_cb(std::move(cb)) {
ZX_DEBUG_ASSERT(conn_ptr);
ZX_DEBUG_ASSERT(result_cb);
}
BrEdrInterrogator::Interrogation::~Interrogation() {
Finish(hci::Status(HostError::kFailed));
}
void BrEdrInterrogator::Interrogation::Finish(hci::Status status) {
// If the connection is gone, we are finished already.
if (!conn_ptr) {
return;
}
// Cancel any callbacks we might receive.
callbacks.clear();
result_cb(status, std::move(conn_ptr));
}
BrEdrInterrogator::BrEdrInterrogator(PeerCache* cache,
fxl::RefPtr<hci::Transport> hci,
async_dispatcher_t* dispatcher)
: hci_(hci),
dispatcher_(dispatcher),
cache_(cache),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(hci_);
ZX_DEBUG_ASSERT(dispatcher_);
ZX_DEBUG_ASSERT(cache_);
}
BrEdrInterrogator::~BrEdrInterrogator() {
for (auto& p : pending_) {
Cancel(p.first);
}
}
void BrEdrInterrogator::Start(PeerId peer_id, hci::ConnectionPtr conn_ptr,
ResultCallback callback) {
ZX_DEBUG_ASSERT(conn_ptr);
ZX_DEBUG_ASSERT(callback);
hci::ConnectionHandle handle = conn_ptr->handle();
auto placed =
pending_.try_emplace(peer_id, std::move(conn_ptr), std::move(callback));
ZX_DEBUG_ASSERT_MSG(placed.second, "interrogating peer %s twice at once",
bt_str(peer_id));
Peer* peer = cache_->FindById(peer_id);
if (!peer) {
Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
if (!peer->name()) {
MakeRemoteNameRequest(peer_id);
}
if (!peer->version()) {
ReadRemoteVersionInformation(peer_id, handle);
}
if (!peer->features().HasPage(0)) {
ReadRemoteFeatures(peer_id, handle);
} else if (peer->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
peer->set_last_page_number(1);
ReadRemoteExtendedFeatures(peer_id, handle, 1);
}
}
void BrEdrInterrogator::Cancel(PeerId peer_id) {
async::PostTask(dispatcher_,
[peer_id, self = weak_ptr_factory_.GetWeakPtr()]() {
if (!self) {
return;
}
auto node = self->pending_.extract(peer_id);
if (!node) {
return;
}
node.mapped().Finish(hci::Status(HostError::kCanceled));
});
}
void BrEdrInterrogator::MaybeComplete(PeerId peer_id) {
Peer* peer = cache_->FindById(peer_id);
if (!peer) {
Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
if (!peer->name()) {
return;
}
if (!peer->version()) {
return;
}
if (!peer->features().HasPage(0)) {
return;
} else if (peer->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
for (uint8_t page = 1; page <= peer->features().last_page_number();
page++) {
if (!peer->features().HasPage(page)) {
return;
}
}
}
Complete(peer_id, hci::Status());
}
void BrEdrInterrogator::Complete(PeerId peer_id, hci::Status status) {
auto node = pending_.extract(peer_id);
ZX_DEBUG_ASSERT(node);
node.mapped().Finish(std::move(status));
}
void BrEdrInterrogator::MakeRemoteNameRequest(PeerId peer_id) {
Peer* peer = cache_->FindById(peer_id);
if (!peer) {
Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
ZX_DEBUG_ASSERT(peer->bredr());
hci::PageScanRepetitionMode mode = hci::PageScanRepetitionMode::kR0;
if (peer->bredr()->page_scan_repetition_mode()) {
mode = *peer->bredr()->page_scan_repetition_mode();
}
auto packet = hci::CommandPacket::New(
hci::kRemoteNameRequest, sizeof(hci::RemoteNameRequestCommandParams));
packet->mutable_view()->mutable_payload_data().SetToZeros();
auto params = packet->mutable_view()
->mutable_payload<hci::RemoteNameRequestCommandParams>();
params->bd_addr = peer->address().value();
params->page_scan_repetition_mode = mode;
if (peer->bredr()->clock_offset()) {
params->clock_offset = *(peer->bredr()->clock_offset());
}
auto it = pending_.find(peer_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second.callbacks.emplace_back([peer_id,
self = weak_ptr_factory_.GetWeakPtr()](
auto, const hci::EventPacket& event) {
if (hci_is_error(event, WARN, "gap-bredr", "remote name request failed")) {
self->Complete(peer_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kRemoteNameRequestCompleteEventCode);
const auto& params =
event.params<hci::RemoteNameRequestCompleteEventParams>();
size_t len = 0;
for (; len < hci::kMaxNameLength; len++) {
if (params.remote_name[len] == 0) {
break;
}
}
Peer* peer = self->cache_->FindById(peer_id);
if (!peer) {
self->Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
peer->SetName(std::string(params.remote_name, params.remote_name + len));
self->MaybeComplete(peer_id);
});
bt_log(SPEW, "gap-bredr", "name request %s", bt_str(peer->address()));
hci_->command_channel()->SendExclusiveCommand(
std::move(packet), dispatcher_, it->second.callbacks.back().callback(),
hci::kRemoteNameRequestCompleteEventCode, {hci::kInquiry});
}
void BrEdrInterrogator::ReadRemoteVersionInformation(
PeerId peer_id, hci::ConnectionHandle handle) {
auto packet =
hci::CommandPacket::New(hci::kReadRemoteVersionInfo,
sizeof(hci::ReadRemoteVersionInfoCommandParams));
packet->mutable_view()
->mutable_payload<hci::ReadRemoteVersionInfoCommandParams>()
->connection_handle = htole16(handle);
auto it = pending_.find(peer_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second.callbacks.emplace_back(
[peer_id, self = weak_ptr_factory_.GetWeakPtr()](
auto, const hci::EventPacket& event) {
if (hci_is_error(event, WARN, "gap-bredr",
"read remote version info failed")) {
self->Complete(peer_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kReadRemoteVersionInfoCompleteEventCode);
const auto params =
event.params<hci::ReadRemoteVersionInfoCompleteEventParams>();
Peer* peer = self->cache_->FindById(peer_id);
if (!peer) {
self->Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
peer->set_version(params.lmp_version, params.manufacturer_name,
params.lmp_subversion);
self->MaybeComplete(peer_id);
});
bt_log(SPEW, "gap-bredr", "asking for version info");
hci_->command_channel()->SendCommand(
std::move(packet), dispatcher_, it->second.callbacks.back().callback(),
hci::kReadRemoteVersionInfoCompleteEventCode);
}
void BrEdrInterrogator::ReadRemoteFeatures(PeerId peer_id,
hci::ConnectionHandle handle) {
auto packet = hci::CommandPacket::New(
hci::kReadRemoteSupportedFeatures,
sizeof(hci::ReadRemoteSupportedFeaturesCommandParams));
packet->mutable_view()
->mutable_payload<hci::ReadRemoteSupportedFeaturesCommandParams>()
->connection_handle = htole16(handle);
auto it = pending_.find(peer_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second.callbacks.emplace_back(
[peer_id, handle, self = weak_ptr_factory_.GetWeakPtr()](
auto, const hci::EventPacket& event) {
if (hci_is_error(event, WARN, "gap-bredr",
"read remote supported features failed")) {
self->Complete(peer_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kReadRemoteSupportedFeaturesCompleteEventCode);
const auto& params =
event.view()
.payload<hci::ReadRemoteSupportedFeaturesCompleteEventParams>();
Peer* peer = self->cache_->FindById(peer_id);
if (!peer) {
self->Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
peer->SetFeaturePage(0, le64toh(params.lmp_features));
if (peer->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
peer->set_last_page_number(1);
self->ReadRemoteExtendedFeatures(peer_id, handle, 1);
}
self->MaybeComplete(peer_id);
});
bt_log(SPEW, "gap-bredr", "asking for supported features");
hci_->command_channel()->SendCommand(
std::move(packet), dispatcher_, it->second.callbacks.back().callback(),
hci::kReadRemoteSupportedFeaturesCompleteEventCode);
}
void BrEdrInterrogator::ReadRemoteExtendedFeatures(PeerId peer_id,
hci::ConnectionHandle handle,
uint8_t page) {
auto packet = hci::CommandPacket::New(
hci::kReadRemoteExtendedFeatures,
sizeof(hci::ReadRemoteExtendedFeaturesCommandParams));
auto params =
packet->mutable_view()
->mutable_payload<hci::ReadRemoteExtendedFeaturesCommandParams>();
params->connection_handle = htole16(handle);
params->page_number = page;
auto it = pending_.find(peer_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second.callbacks.emplace_back([peer_id, handle, page,
self = weak_ptr_factory_.GetWeakPtr()](
auto, const auto& event) {
if (hci_is_error(event, WARN, "gap-bredr",
"read remote extended features failed")) {
self->Complete(peer_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kReadRemoteExtendedFeaturesCompleteEventCode);
const auto& params =
event.view()
.template payload<
hci::ReadRemoteExtendedFeaturesCompleteEventParams>();
Peer* peer = self->cache_->FindById(peer_id);
if (!peer) {
self->Complete(peer_id, hci::Status(HostError::kFailed));
return;
}
peer->SetFeaturePage(params.page_number, le64toh(params.lmp_features));
if (params.page_number != page) {
bt_log(INFO, "gap-bredr", "requested page %u and got page %u, giving up",
page, params.page_number);
peer->set_last_page_number(0);
} else {
peer->set_last_page_number(params.max_page_number);
}
if (params.page_number < peer->features().last_page_number()) {
self->ReadRemoteExtendedFeatures(peer_id, handle, params.page_number + 1);
}
self->MaybeComplete(peer_id);
});
bt_log(SPEW, "gap-bredr", "get ext page %u", page);
hci_->command_channel()->SendCommand(
std::move(packet), dispatcher_, it->second.callbacks.back().callback(),
hci::kReadRemoteExtendedFeaturesCompleteEventCode);
}
} // namespace gap
} // namespace bt