| // 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/hci.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/transport.h" |
| |
| namespace bt::gap { |
| |
| BrEdrInterrogator::BrEdrInterrogator(PeerCache* cache, fxl::WeakPtr<hci::Transport> hci, |
| async_dispatcher_t* dispatcher) |
| : Interrogator(cache, std::move(hci), dispatcher), 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", page, |
| params.page_number); |
| 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 |