blob: 815b312a060bc15fc26a999a93e88bca48d40615 [file] [log] [blame] [edit]
// Copyright 2017 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 "adapter.h"
#include <endian.h>
#include "garnet/drivers/bluetooth/lib/hci/connection.h"
#include "garnet/drivers/bluetooth/lib/hci/legacy_low_energy_advertiser.h"
#include "garnet/drivers/bluetooth/lib/hci/low_energy_connector.h"
#include "garnet/drivers/bluetooth/lib/hci/sequential_command_runner.h"
#include "garnet/drivers/bluetooth/lib/hci/transport.h"
#include "garnet/drivers/bluetooth/lib/hci/util.h"
#include "garnet/drivers/bluetooth/lib/l2cap/channel_manager.h"
#include "lib/fxl/random/uuid.h"
#include "bredr_connection_manager.h"
#include "bredr_discovery_manager.h"
#include "low_energy_advertising_manager.h"
#include "low_energy_connection_manager.h"
#include "low_energy_discovery_manager.h"
#include "remote_device.h"
namespace btlib {
namespace gap {
Adapter::Adapter(fxl::RefPtr<hci::Transport> hci,
fbl::RefPtr<l2cap::L2CAP> l2cap,
fbl::RefPtr<gatt::GATT> gatt)
: identifier_(fxl::GenerateUUID()),
dispatcher_(async_get_default()),
hci_(hci),
init_state_(State::kNotInitialized),
l2cap_(l2cap),
gatt_(gatt),
weak_ptr_factory_(this) {
FXL_DCHECK(hci_);
FXL_DCHECK(l2cap_);
FXL_DCHECK(gatt_);
FXL_DCHECK(dispatcher_)
<< "gap: Adapter: Must be created on a thread with a dispatcher";
init_seq_runner_ =
std::make_unique<hci::SequentialCommandRunner>(dispatcher_, hci_);
auto self = weak_ptr_factory_.GetWeakPtr();
hci_->SetTransportClosedCallback(
[self] {
if (self)
self->OnTransportClosed();
},
dispatcher_);
}
Adapter::~Adapter() {
if (IsInitialized())
ShutDown();
}
bool Adapter::Initialize(const InitializeCallback& callback,
const fxl::Closure& transport_closed_cb) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(callback);
FXL_DCHECK(transport_closed_cb);
if (IsInitialized()) {
FXL_LOG(WARNING) << "gap: Adapter: Already initialized";
return false;
}
FXL_DCHECK(!IsInitializing());
init_state_ = State::kInitializing;
FXL_DCHECK(init_seq_runner_->IsReady());
FXL_DCHECK(!init_seq_runner_->HasQueuedCommands());
transport_closed_cb_ = transport_closed_cb;
// Start by resetting the controller to a clean state and then send
// informational parameter commands that are not specific to LE or BR/EDR. The
// commands sent here are mandatory for all LE controllers.
//
// NOTE: It's safe to pass capture |this| directly in the callbacks as
// |init_seq_runner_| will internally invalidate the callbacks if it ever gets
// deleted.
// HCI_Reset
init_seq_runner_->QueueCommand(hci::CommandPacket::New(hci::kReset));
// HCI_Read_Local_Version_Information
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kReadLocalVersionInfo),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete.return_params<hci::ReadLocalVersionInfoReturnParams>();
state_.hci_version_ = params->hci_version;
});
// HCI_Read_Local_Supported_Commands
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kReadLocalSupportedCommands),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete
.return_params<hci::ReadLocalSupportedCommandsReturnParams>();
std::memcpy(state_.supported_commands_, params->supported_commands,
sizeof(params->supported_commands));
});
// HCI_Read_Local_Supported_Features
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kReadLocalSupportedFeatures),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete
.return_params<hci::ReadLocalSupportedFeaturesReturnParams>();
state_.lmp_features_[0] = le64toh(params->lmp_features);
});
// HCI_Read_BD_ADDR
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kReadBDADDR),
[this](const hci::EventPacket& cmd_complete) {
auto params = cmd_complete.return_params<hci::ReadBDADDRReturnParams>();
state_.controller_address_ = params->bd_addr;
});
init_seq_runner_->RunCommands([callback, this](hci::Status status) {
if (!status) {
FXL_LOG(ERROR)
<< "gap: Adapter: Failed to obtain initial controller information: "
<< status.ToString();
CleanUp();
callback(false);
return;
}
InitializeStep2(callback);
});
return true;
}
void Adapter::ShutDown() {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_VLOG(1) << "gap: shutting down";
if (IsInitializing()) {
FXL_DCHECK(!init_seq_runner_->IsReady());
init_seq_runner_->Cancel();
}
CleanUp();
}
bool Adapter::IsDiscovering() const {
return (le_discovery_manager_ && le_discovery_manager_->discovering()) ||
(bredr_discovery_manager_ && bredr_discovery_manager_->discovering());
}
void Adapter::SetLocalName(std::string name, hci::StatusCallback callback) {
// TODO(jamuraa): set the public LE advertisement name from |name|
bool null_term = true;
if (name.size() >= hci::kMaxLocalNameLength) {
name.resize(hci::kMaxLocalNameLength);
null_term = false;
}
auto write_name = hci::CommandPacket::New(
hci::kWriteLocalName, sizeof(hci::WriteLocalNameCommandParams));
auto name_buf = common::MutableBufferView(
write_name->mutable_view()
->mutable_payload<hci::WriteLocalNameCommandParams>()
->local_name,
hci::kMaxLocalNameLength);
name_buf.Write(reinterpret_cast<const uint8_t*>(name.data()), name.size());
if (null_term) {
name_buf[name.size()] = 0;
}
hci_->command_channel()->SendCommand(
std::move(write_name), dispatcher_,
[cb = std::move(callback)](auto, const hci::EventPacket& event) mutable {
auto status = event.ToStatus();
if (!status) {
FXL_LOG(WARNING) << "gap: Adapter: set local name failure: "
<< status.ToString();
}
cb(status);
});
}
void Adapter::InitializeStep2(const InitializeCallback& callback) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(IsInitializing());
// Low Energy MUST be supported. We don't support BR/EDR-only controllers.
if (!state_.IsLowEnergySupported()) {
FXL_LOG(ERROR)
<< "gap: Adapter: Bluetooth Low Energy not supported by controller";
CleanUp();
callback(false);
return;
}
// Check the HCI version. We officially only support 4.2+ only but for now we
// just log a warning message if the version is legacy.
if (state_.hci_version() < hci::HCIVersion::k4_2) {
FXL_LOG(WARNING) << "gap: Adapter: controller is using legacy HCI version: "
<< hci::HCIVersionToString(state_.hci_version());
}
FXL_DCHECK(init_seq_runner_->IsReady());
// If the controller supports the Read Buffer Size command then send it.
// Otherwise we'll default to 0 when initializing the ACLDataChannel.
if (state_.IsCommandSupported(14, hci::SupportedCommand::kReadBufferSize)) {
// HCI_Read_Buffer_Size
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kReadBufferSize),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete.return_params<hci::ReadBufferSizeReturnParams>();
uint16_t mtu = le16toh(params->hc_acl_data_packet_length);
uint16_t max_count = le16toh(params->hc_total_num_acl_data_packets);
if (mtu && max_count) {
state_.bredr_data_buffer_info_ =
hci::DataBufferInfo(mtu, max_count);
}
});
}
// HCI_LE_Read_Local_Supported_Features
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kLEReadLocalSupportedFeatures),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete
.return_params<hci::LEReadLocalSupportedFeaturesReturnParams>();
state_.le_state_.supported_features_ = le64toh(params->le_features);
});
// HCI_LE_Read_Supported_States
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kLEReadSupportedStates),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete
.return_params<hci::LEReadSupportedStatesReturnParams>();
state_.le_state_.supported_states_ = le64toh(params->le_states);
});
// HCI_LE_Read_Buffer_Size
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci::kLEReadBufferSize),
[this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete.return_params<hci::LEReadBufferSizeReturnParams>();
uint16_t mtu = le16toh(params->hc_le_acl_data_packet_length);
uint8_t max_count = params->hc_total_num_le_acl_data_packets;
if (mtu && max_count) {
state_.le_state_.data_buffer_info_ =
hci::DataBufferInfo(mtu, max_count);
}
});
if (state_.HasLMPFeatureBit(0u, hci::LMPFeature::kSecureSimplePairing)) {
// HCI_Write_Simple_Pairing_Mode
auto write_ssp = hci::CommandPacket::New(
hci::kWriteSimplePairingMode,
sizeof(hci::WriteSimplePairingModeCommandParams));
write_ssp->mutable_view()
->mutable_payload<hci::WriteSimplePairingModeCommandParams>()
->simple_pairing_mode = hci::GenericEnableParam::kEnable;
init_seq_runner_->QueueCommand(std::move(write_ssp), [](const auto& event) {
auto status = event.ToStatus();
if (!status) {
FXL_LOG(WARNING) << "gap: Adapter: Couldn't set simple pairing mode: "
<< status.ToString();
}
});
}
// If there are extended features then try to read the first page of the
// extended features.
if (state_.HasLMPFeatureBit(0u, hci::LMPFeature::kExtendedFeatures)) {
// Page index 1 must be available.
state_.max_lmp_feature_page_index_ = 1;
// HCI_Read_Local_Extended_Features
auto cmd_packet = hci::CommandPacket::New(
hci::kReadLocalExtendedFeatures,
sizeof(hci::ReadLocalExtendedFeaturesCommandParams));
// Try to read page 1.
cmd_packet->mutable_view()
->mutable_payload<hci::ReadLocalExtendedFeaturesCommandParams>()
->page_number = 1;
init_seq_runner_->QueueCommand(
std::move(cmd_packet), [this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete
.return_params<hci::ReadLocalExtendedFeaturesReturnParams>();
state_.lmp_features_[1] = le64toh(params->extended_lmp_features);
state_.max_lmp_feature_page_index_ = params->maximum_page_number;
});
}
init_seq_runner_->RunCommands([callback, this](hci::Status status) {
if (!status) {
FXL_LOG(ERROR) << "gap: Adapter: Failed to obtain initial controller "
"information (step 2): "
<< status.ToString();
CleanUp();
callback(false);
return;
}
InitializeStep3(callback);
});
}
void Adapter::InitializeStep3(const InitializeCallback& callback) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(IsInitializing());
if (!state_.bredr_data_buffer_info().IsAvailable() &&
!state_.low_energy_state().data_buffer_info().IsAvailable()) {
FXL_LOG(ERROR)
<< "gap: Adapter: Both BR/EDR and LE buffers are unavailable";
CleanUp();
callback(false);
return;
}
// Now that we have all the ACL data buffer information it's time to
// initialize the ACLDataChannel.
if (!hci_->InitializeACLDataChannel(
state_.bredr_data_buffer_info(),
state_.low_energy_state().data_buffer_info())) {
FXL_LOG(ERROR)
<< "gap: Adapter: Failed to initialize ACLDataChannel (step 3)";
CleanUp();
callback(false);
return;
}
FXL_DCHECK(init_seq_runner_->IsReady());
FXL_DCHECK(!init_seq_runner_->HasQueuedCommands());
// HCI_Set_Event_Mask
{
uint64_t event_mask = BuildEventMask();
auto cmd_packet = hci::CommandPacket::New(
hci::kSetEventMask, sizeof(hci::SetEventMaskCommandParams));
cmd_packet->mutable_view()
->mutable_payload<hci::SetEventMaskCommandParams>()
->event_mask = htole64(event_mask);
init_seq_runner_->QueueCommand(std::move(cmd_packet));
}
// HCI_LE_Set_Event_Mask
{
uint64_t event_mask = BuildLEEventMask();
auto cmd_packet = hci::CommandPacket::New(
hci::kLESetEventMask, sizeof(hci::LESetEventMaskCommandParams));
cmd_packet->mutable_view()
->mutable_payload<hci::LESetEventMaskCommandParams>()
->le_event_mask = htole64(event_mask);
init_seq_runner_->QueueCommand(std::move(cmd_packet));
}
// HCI_Write_LE_Host_Support if the appropriate feature bit is not set AND if
// the controller supports this command.
if (!state_.HasLMPFeatureBit(1, hci::LMPFeature::kLESupportedHost) &&
state_.IsCommandSupported(24,
hci::SupportedCommand::kWriteLEHostSupport)) {
auto cmd_packet = hci::CommandPacket::New(
hci::kWriteLEHostSupport, sizeof(hci::WriteLEHostSupportCommandParams));
auto params = cmd_packet->mutable_view()
->mutable_payload<hci::WriteLEHostSupportCommandParams>();
params->le_supported_host = hci::GenericEnableParam::kEnable;
params->simultaneous_le_host = 0x00;
init_seq_runner_->QueueCommand(std::move(cmd_packet));
}
// If we know that Page 2 of the extended features bitfield is available, then
// request it.
if (state_.max_lmp_feature_page_index_ > 1) {
auto cmd_packet = hci::CommandPacket::New(
hci::kReadLocalExtendedFeatures,
sizeof(hci::ReadLocalExtendedFeaturesCommandParams));
// Try to read page 2.
cmd_packet->mutable_view()
->mutable_payload<hci::ReadLocalExtendedFeaturesCommandParams>()
->page_number = 2;
// HCI_Read_Local_Extended_Features
init_seq_runner_->QueueCommand(
std::move(cmd_packet), [this](const hci::EventPacket& cmd_complete) {
auto params =
cmd_complete
.return_params<hci::ReadLocalExtendedFeaturesReturnParams>();
state_.lmp_features_[2] = le64toh(params->extended_lmp_features);
state_.max_lmp_feature_page_index_ = params->maximum_page_number;
});
}
init_seq_runner_->RunCommands([callback, this](hci::Status status) {
if (!status) {
FXL_LOG(ERROR) << "gap: Adapter: Failed to obtain initial controller "
"information (step 3): "
<< status.ToString();
CleanUp();
callback(false);
return;
}
InitializeStep4(callback);
});
}
void Adapter::InitializeStep4(const InitializeCallback& callback) {
FXL_DCHECK(IsInitializing());
// Initialize the scan manager based on current feature support.
if (state_.low_energy_state().IsFeatureSupported(
hci::LESupportedFeature::kLEExtendedAdvertising)) {
FXL_LOG(INFO) << "gap: adapter: controller supports extended advertising";
FXL_LOG(INFO) << "gap: adapter: host doesn't support 5.0 extended features,"
<< " defaulting to legacy procedures.";
// TODO(armansito): Initialize |hci_le_*| objects here with extended-mode
// versions.
}
// Called by |hci_le_connector_| when a connection was created due to an
// incoming connection. This callback routes the received |link| to
// |hci_le_advertiser_| for it to be matched to an advertisement instance.
auto self = weak_ptr_factory_.GetWeakPtr();
auto incoming_conn_cb = [self](std::unique_ptr<hci::Connection> link) {
if (self && self->hci_le_advertiser_) {
self->hci_le_advertiser_->OnIncomingConnection(std::move(link));
}
};
hci_le_advertiser_ = std::make_unique<hci::LegacyLowEnergyAdvertiser>(hci_);
hci_le_connector_ = std::make_unique<hci::LowEnergyConnector>(
hci_, dispatcher_, std::move(incoming_conn_cb));
le_discovery_manager_ = std::make_unique<LowEnergyDiscoveryManager>(
Mode::kLegacy, hci_, &device_cache_);
le_connection_manager_ = std::make_unique<LowEnergyConnectionManager>(
hci_, hci_le_connector_.get(), &device_cache_, l2cap_, gatt_);
le_advertising_manager_ =
std::make_unique<LowEnergyAdvertisingManager>(hci_le_advertiser_.get());
bredr_connection_manager_ = std::make_unique<BrEdrConnectionManager>(
hci_, &device_cache_,
state_.HasLMPFeatureBit(0, hci::LMPFeature::kInterlacedPageScan));
bredr_discovery_manager_ =
std::make_unique<BrEdrDiscoveryManager>(hci_, &device_cache_);
// This completes the initialization sequence.
self->init_state_ = State::kInitialized;
callback(true);
}
uint64_t Adapter::BuildEventMask() {
uint64_t event_mask = 0;
// Enable events that are needed for basic functionality.
event_mask |= static_cast<uint64_t>(hci::EventMask::kConnectionCompleteEvent);
event_mask |= static_cast<uint64_t>(hci::EventMask::kConnectionRequestEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kDisconnectionCompleteEvent);
event_mask |= static_cast<uint64_t>(hci::EventMask::kHardwareErrorEvent);
event_mask |= static_cast<uint64_t>(hci::EventMask::kLEMetaEvent);
event_mask |= static_cast<uint64_t>(hci::EventMask::kInquiryCompleteEvent);
event_mask |= static_cast<uint64_t>(hci::EventMask::kInquiryResultEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kInquiryResultWithRSSIEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kExtendedInquiryResultEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kIOCapabilityRequestEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kIOCapabilityResponseEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kUserConfirmationRequestEvent);
event_mask |= static_cast<uint64_t>(hci::EventMask::kUserPasskeyRequestEvent);
event_mask |=
static_cast<uint64_t>(hci::EventMask::kRemoteOOBDataRequestEvent);
return event_mask;
}
uint64_t Adapter::BuildLEEventMask() {
uint64_t event_mask = 0;
event_mask |= static_cast<uint64_t>(hci::LEEventMask::kLEConnectionComplete);
event_mask |= static_cast<uint64_t>(hci::LEEventMask::kLEAdvertisingReport);
event_mask |=
static_cast<uint64_t>(hci::LEEventMask::kLEConnectionUpdateComplete);
return event_mask;
}
void Adapter::CleanUp() {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
init_state_ = State::kNotInitialized;
state_ = AdapterState();
transport_closed_cb_ = nullptr;
bredr_discovery_manager_ = nullptr;
le_advertising_manager_ = nullptr;
le_connection_manager_ = nullptr;
le_discovery_manager_ = nullptr;
hci_le_connector_ = nullptr;
hci_le_advertiser_ = nullptr;
// TODO(armansito): hci::Transport::ShutDown() should send a shutdown message
// to the bt-hci device, which would be responsible for sending HCI_Reset upon
// exit.
if (hci_->IsInitialized())
hci_->ShutDown();
}
void Adapter::OnTransportClosed() {
FXL_LOG(INFO) << "gap: Adapter: HCI transport was closed";
if (transport_closed_cb_)
transport_closed_cb_();
}
} // namespace gap
} // namespace btlib