blob: 5d6ee36ecd3eac1311f3a5beedafcf1176073804 [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 device_id, hci::ConnectionPtr conn_ptr,
ResultCallback callback) {
ZX_DEBUG_ASSERT(conn_ptr);
ZX_DEBUG_ASSERT(callback);
hci::ConnectionHandle handle = conn_ptr->handle();
pending_.emplace(device_id, std::make_unique<Interrogation>(
std::move(conn_ptr), std::move(callback)));
Peer* device = cache_->FindById(device_id);
if (!device) {
Complete(device_id, hci::Status(HostError::kFailed));
return;
}
if (!device->name()) {
MakeRemoteNameRequest(device_id);
}
if (!device->version()) {
ReadRemoteVersionInformation(device_id, handle);
}
if (!device->features().HasPage(0)) {
ReadRemoteFeatures(device_id, handle);
} else if (device->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
device->set_last_page_number(1);
ReadRemoteExtendedFeatures(device_id, handle, 1);
}
}
void BrEdrInterrogator::Cancel(PeerId device_id) {
async::PostTask(dispatcher_, [id = std::move(device_id),
self = weak_ptr_factory_.GetWeakPtr()]() {
if (!self) {
return;
}
auto it = self->pending_.find(id);
if (it == self->pending_.end()) {
return;
}
it->second->Finish(hci::Status(HostError::kCanceled));
self->pending_.erase(it);
});
}
void BrEdrInterrogator::MaybeComplete(PeerId device_id) {
Peer* device = cache_->FindById(device_id);
if (!device) {
Complete(device_id, hci::Status(HostError::kFailed));
return;
}
if (!device->name()) {
return;
}
if (!device->version()) {
return;
}
if (!device->features().HasPage(0)) {
return;
} else if (device->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
for (uint8_t page = 1; page <= device->features().last_page_number();
page++) {
if (!device->features().HasPage(page)) {
return;
}
}
}
Complete(device_id, hci::Status());
}
void BrEdrInterrogator::Complete(PeerId device_id, hci::Status status) {
auto it = pending_.find(std::move(device_id));
ZX_DEBUG_ASSERT(it != pending_.end());
it->second->Finish(std::move(status));
pending_.erase(it);
}
void BrEdrInterrogator::MakeRemoteNameRequest(PeerId device_id) {
Peer* device = cache_->FindById(device_id);
if (!device) {
Complete(device_id, hci::Status(HostError::kFailed));
return;
}
ZX_DEBUG_ASSERT(device->bredr());
hci::PageScanRepetitionMode mode = hci::PageScanRepetitionMode::kR0;
if (device->bredr()->page_scan_repetition_mode()) {
mode = *device->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 = device->address().value();
params->page_scan_repetition_mode = mode;
if (device->bredr()->clock_offset()) {
params->clock_offset = *(device->bredr()->clock_offset());
}
auto it = pending_.find(device_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second->callbacks.emplace_back([device_id,
self = weak_ptr_factory_.GetWeakPtr()](
auto, const auto& event) {
if (hci_is_error(event, WARN, "gap-bredr", "remote name request failed")) {
self->Complete(device_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kRemoteNameRequestCompleteEventCode);
const auto& params =
event.view()
.template payload<hci::RemoteNameRequestCompleteEventParams>();
size_t len = 0;
for (; len < hci::kMaxNameLength; len++) {
if (params.remote_name[len] == 0) {
break;
}
}
Peer* device = self->cache_->FindById(device_id);
if (!device) {
self->Complete(device_id, hci::Status(HostError::kFailed));
return;
}
device->SetName(std::string(params.remote_name, params.remote_name + len));
self->MaybeComplete(device_id);
});
bt_log(SPEW, "gap-bredr", "name request %s",
device->address().ToString().c_str());
hci_->command_channel()->SendExclusiveCommand(
std::move(packet), dispatcher_, it->second->callbacks.back().callback(),
hci::kRemoteNameRequestCompleteEventCode, {hci::kInquiry});
}
void BrEdrInterrogator::ReadRemoteVersionInformation(
PeerId device_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(device_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second->callbacks.emplace_back([device_id,
self = weak_ptr_factory_.GetWeakPtr()](
auto, const auto& event) {
if (hci_is_error(event, WARN, "gap-bredr",
"read remote version info failed")) {
self->Complete(device_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kReadRemoteVersionInfoCompleteEventCode);
const auto params =
event.view()
.template payload<hci::ReadRemoteVersionInfoCompleteEventParams>();
Peer* device = self->cache_->FindById(device_id);
if (!device) {
self->Complete(device_id, hci::Status(HostError::kFailed));
return;
}
device->set_version(params.lmp_version, params.manufacturer_name,
params.lmp_subversion);
self->MaybeComplete(device_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 device_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(device_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second->callbacks.emplace_back(
[device_id, handle, self = weak_ptr_factory_.GetWeakPtr()](
auto, const auto& event) {
if (hci_is_error(event, WARN, "gap-bredr",
"read remote supported features failed")) {
self->Complete(device_id, event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_DEBUG_ASSERT(event.event_code() ==
hci::kReadRemoteSupportedFeaturesCompleteEventCode);
const auto& params =
event.view()
.template payload<
hci::ReadRemoteSupportedFeaturesCompleteEventParams>();
Peer* device = self->cache_->FindById(device_id);
if (!device) {
self->Complete(device_id, hci::Status(HostError::kFailed));
return;
}
device->SetFeaturePage(0, le64toh(params.lmp_features));
if (device->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
device->set_last_page_number(1);
self->ReadRemoteExtendedFeatures(device_id, handle, 1);
}
self->MaybeComplete(device_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 device_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(device_id);
ZX_DEBUG_ASSERT(it != pending_.end());
it->second->callbacks.emplace_back([device_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(device_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* device = self->cache_->FindById(device_id);
if (!device) {
self->Complete(device_id, hci::Status(HostError::kFailed));
return;
}
device->SetFeaturePage(params.page_number, le64toh(params.lmp_features));
if (params.page_number != page) {
bt_log(INFO, "gap-bredr",
"requested page %u and received page %u, giving up", page,
params.page_number);
device->set_last_page_number(0);
} else {
device->set_last_page_number(params.max_page_number);
}
if (params.page_number < device->features().last_page_number()) {
self->ReadRemoteExtendedFeatures(device_id, handle,
params.page_number + 1);
}
self->MaybeComplete(device_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