blob: 2ca5171bd984dca43d06821f7bd0dba4b853a8ea [file] [log] [blame]
// 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 "connection.h"
#include <endian.h>
#include "garnet/drivers/bluetooth/lib/hci/control_packets.h"
#include "garnet/drivers/bluetooth/lib/hci/defaults.h"
#include "garnet/drivers/bluetooth/lib/hci/transport.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_printf.h"
namespace bluetooth {
namespace hci {
namespace {
std::string LinkTypeToString(Connection::LinkType type) {
switch (type) {
case Connection::LinkType::kACL:
return "ACL";
case Connection::LinkType::kSCO:
return "SCO";
case Connection::LinkType::kESCO:
return "ESCO";
case Connection::LinkType::kLE:
return "LE";
}
FXL_NOTREACHED();
return "(invalid)";
}
} // namespace
Connection::LowEnergyParameters::LowEnergyParameters(
uint16_t interval_min,
uint16_t interval_max,
uint16_t interval,
uint16_t latency,
uint16_t supervision_timeout)
: interval_min_(interval_min),
interval_max_(interval_max),
interval_(interval),
latency_(latency),
supervision_timeout_(supervision_timeout) {
FXL_DCHECK(interval_min_ <= interval_max_);
}
Connection::LowEnergyParameters::LowEnergyParameters()
: interval_min_(defaults::kLEConnectionIntervalMin),
interval_max_(defaults::kLEConnectionIntervalMax),
interval_(0x0000),
latency_(0x0000),
supervision_timeout_(defaults::kLESupervisionTimeout) {}
bool Connection::LowEnergyParameters::operator==(
const LowEnergyParameters& other) const {
return other.interval_min_ == interval_min_ &&
other.interval_max_ == interval_max_ && other.interval_ == interval_ &&
other.latency_ == latency_ &&
other.supervision_timeout_ == supervision_timeout_;
}
Connection::Connection(ConnectionHandle handle,
Role role,
const common::DeviceAddress& peer_address,
const LowEnergyParameters& params,
fxl::RefPtr<Transport> hci)
: ll_type_(LinkType::kLE),
handle_(handle),
role_(role),
is_open_(true),
peer_address_(peer_address),
le_params_(params),
hci_(hci),
weak_ptr_factory_(this) {
FXL_DCHECK(handle);
FXL_DCHECK(le_params_.interval());
FXL_DCHECK(hci_);
}
Connection::~Connection() {
Close();
}
void Connection::Close(Status reason) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
if (!is_open())
return;
// The connection is immediately marked as closed as there is no reasonable
// way for a Disconnect procedure to fail, i.e. it always succeeds. If the
// controller reports failure in the Disconnection Complete event, it should
// be because we gave it an already disconnected handle which we would treat
// as success.
//
// TODO(armansito): The procedure could also fail if "the command was not
// presently allowed". Retry in that case?
set_closed();
// Here we send a HCI_Disconnect command without waiting for it to complete.
auto status_cb = [](auto id, const EventPacket& event) {
FXL_DCHECK(event.event_code() == kCommandStatusEventCode);
const auto& params = event.view().payload<CommandStatusEventParams>();
if (params.status != Status::kSuccess) {
FXL_LOG(WARNING) << fxl::StringPrintf(
"Ignoring failed disconnection status: 0x%02x", params.status);
}
};
auto disconn =
CommandPacket::New(kDisconnect, sizeof(DisconnectCommandParams));
auto params =
disconn->mutable_view()->mutable_payload<DisconnectCommandParams>();
params->connection_handle = htole16(handle_);
params->reason = reason;
hci_->command_channel()->SendCommand(
std::move(disconn), fsl::MessageLoop::GetCurrent()->task_runner(),
status_cb, {}, kCommandStatusEventCode);
}
std::string Connection::ToString() const {
return fxl::StringPrintf(
"(%s link - handle: 0x%04x, role: %s, address: %s, "
"interval: %.2f ms, latency: %.2f ms, timeout: %u ms)",
LinkTypeToString(ll_type_).c_str(), handle_,
role_ == Role::kMaster ? "master" : "slave",
peer_address_.ToString().c_str(),
static_cast<float>(le_params_.interval()) * 1.25f,
static_cast<float>(le_params_.latency()) * 1.25f,
le_params_.supervision_timeout() * 10u);
}
} // namespace hci
} // namespace bluetooth