blob: 5767cb5eb2627d28bfbe750ba772365eb63b4a75 [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-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/transport.h"
namespace bt::gap {
BrEdrInterrogator::BrEdrInterrogator(PeerCache* cache, fxl::WeakPtr<hci::Transport> hci)
: Interrogator(cache, std::move(hci)), weak_ptr_factory_(this) {}
void BrEdrInterrogator::SendCommands(InterrogationRefPtr interrogation) {
Peer* peer = peer_cache()->FindById(interrogation->peer_id());
if (!peer) {
interrogation->Complete(hci::Status(HostError::kFailed));
return;
}
if (!peer->name()) {
MakeRemoteNameRequest(interrogation);
}
if (!peer->version()) {
ReadRemoteVersionInformation(interrogation);
}
if (!peer->features().HasPage(0)) {
ReadRemoteFeatures(interrogation);
} else if (peer->features().HasBit(0, hci::LMPFeature::kExtendedFeatures)) {
ReadRemoteExtendedFeatures(interrogation, 1);
}
}
void BrEdrInterrogator::MakeRemoteNameRequest(InterrogationRefPtr interrogation) {
Peer* peer = peer_cache()->FindById(interrogation->peer_id());
if (!peer) {
interrogation->Complete(hci::Status(HostError::kFailed));
return;
}
ZX_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_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 cmd_cb = [interrogation, self = weak_ptr_factory_.GetWeakPtr()](
auto id, const hci::EventPacket& event) {
if (!interrogation->active()) {
return;
}
if (hci_is_error(event, WARN, "gap-bredr", "remote name request failed")) {
interrogation->Complete(event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_ASSERT(event.event_code() == hci::kRemoteNameRequestCompleteEventCode);
bt_log(TRACE, "gap-bredr", "name request complete (peer id: %s)",
bt_str(interrogation->peer_id()));
const auto& params = event.params<hci::RemoteNameRequestCompleteEventParams>();
Peer* const peer = self->peer_cache()->FindById(interrogation->peer_id());
if (!peer) {
interrogation->Complete(hci::Status(HostError::kFailed));
return;
}
const auto remote_name_end = std::find(params.remote_name, std::end(params.remote_name), '\0');
peer->SetName(std::string(params.remote_name, remote_name_end));
};
bt_log(TRACE, "gap-bredr", "sending name request (peer id: %s)",
bt_str(interrogation->peer_id()));
hci()->command_channel()->SendExclusiveCommand(std::move(packet), std::move(cmd_cb),
hci::kRemoteNameRequestCompleteEventCode,
{hci::kInquiry});
}
void BrEdrInterrogator::ReadRemoteFeatures(InterrogationRefPtr interrogation) {
auto packet = hci::CommandPacket::New(hci::kReadRemoteSupportedFeatures,
sizeof(hci::ReadRemoteSupportedFeaturesCommandParams));
packet->mutable_payload<hci::ReadRemoteSupportedFeaturesCommandParams>()->connection_handle =
htole16(interrogation->handle());
auto cmd_cb = [interrogation, self = weak_ptr_factory_.GetWeakPtr()](
auto id, const hci::EventPacket& event) {
if (!interrogation->active()) {
return;
}
if (hci_is_error(event, WARN, "gap-bredr", "read remote supported features failed")) {
interrogation->Complete(event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_ASSERT(event.event_code() == hci::kReadRemoteSupportedFeaturesCompleteEventCode);
bt_log(TRACE, "gap-bredr", "remote features request complete (peer id: %s)",
bt_str(interrogation->peer_id()));
const auto& params =
event.view().payload<hci::ReadRemoteSupportedFeaturesCompleteEventParams>();
Peer* peer = self->peer_cache()->FindById(interrogation->peer_id());
if (!peer) {
interrogation->Complete(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(interrogation, 1);
}
};
bt_log(TRACE, "gap-bredr", "asking for supported features (peer id: %s)",
bt_str(interrogation->peer_id()));
hci()->command_channel()->SendCommand(std::move(packet), std::move(cmd_cb),
hci::kReadRemoteSupportedFeaturesCompleteEventCode);
}
void BrEdrInterrogator::ReadRemoteExtendedFeatures(InterrogationRefPtr interrogation,
uint8_t page) {
auto packet = hci::CommandPacket::New(hci::kReadRemoteExtendedFeatures,
sizeof(hci::ReadRemoteExtendedFeaturesCommandParams));
auto params = packet->mutable_payload<hci::ReadRemoteExtendedFeaturesCommandParams>();
params->connection_handle = htole16(interrogation->handle());
params->page_number = page;
auto cmd_cb = [interrogation, page, self = weak_ptr_factory_.GetWeakPtr()](auto,
const auto& event) {
if (!interrogation->active()) {
return;
}
if (hci_is_error(event, WARN, "gap-bredr", "read remote extended features failed (peer id: %s)",
bt_str(interrogation->peer_id()))) {
interrogation->Complete(event.ToStatus());
return;
}
if (event.event_code() == hci::kCommandStatusEventCode) {
return;
}
ZX_ASSERT(event.event_code() == hci::kReadRemoteExtendedFeaturesCompleteEventCode);
const auto& params =
event.view().template payload<hci::ReadRemoteExtendedFeaturesCompleteEventParams>();
Peer* peer = self->peer_cache()->FindById(interrogation->peer_id());
if (!peer) {
interrogation->Complete(hci::Status(HostError::kFailed));
return;
}
bt_log(TRACE, "gap-bredr",
"got extended features page %u, max page %u (requested page: %u, peer id: %s)",
params.page_number, params.max_page_number, page, bt_str(interrogation->peer_id()));
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 (peer: %s)", page,
params.page_number, bt_str(interrogation->peer_id()));
peer->set_last_page_number(0);
return;
}
// NOTE: last page number will be capped at 2
peer->set_last_page_number(params.max_page_number);
if (page < peer->features().last_page_number()) {
self->ReadRemoteExtendedFeatures(interrogation, page + 1);
}
};
bt_log(TRACE, "gap-bredr", "requesting extended features page %u (peer id: %s)", page,
bt_str(interrogation->peer_id()));
hci()->command_channel()->SendCommand(std::move(packet), std::move(cmd_cb),
hci::kReadRemoteExtendedFeaturesCompleteEventCode);
} // namespace gap
} // namespace bt::gap