blob: 97db291b6e4f6b28a156c86bd8a9c8a51a97fd39 [file] [log] [blame]
// Copyright 2019 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_connection_request.h"
#include "src/connectivity/bluetooth/core/bt-host/common/identifier.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/defaults.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/hci.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/status.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/util.h"
namespace bt {
namespace hci {
std::unique_ptr<CommandPacket> CreateConnectionPacket(
DeviceAddress address,
std::optional<PageScanRepetitionMode> page_scan_repetition_mode,
std::optional<uint16_t> clock_offset) {
auto request = CommandPacket::New(kCreateConnection,
sizeof(CreateConnectionCommandParams));
auto params =
request->mutable_view()->mutable_payload<CreateConnectionCommandParams>();
params->bd_addr = address.value();
params->packet_type = htole16(kEnableAllPacketTypes);
// The Page Scan Repetition Mode of the remote device as retrieved by Inquiry.
// If we do not have one for the device, opt for R2 so we will send for at
// least 2.56s
if (page_scan_repetition_mode)
params->page_scan_repetition_mode = *page_scan_repetition_mode;
else
params->page_scan_repetition_mode =
PageScanRepetitionMode::kR2; // Every 2.56 seconds
params->reserved = 0; // Reserved, must be set to 0.
// Clock Offset. The lower 15 bits are set to the clock offset as retrieved
// by an Inquiry. The highest bit is set to 1 if the rest of this parameter
// is valid. If we don't have one, use the default.
if (clock_offset)
params->clock_offset = htole16(*clock_offset);
else
params->clock_offset = 0x0000;
params->allow_role_switch = static_cast<uint8_t>(
RoleSwitchBits::kDisallowRoleSwitch); // Do not allow role switch
return request;
}
void BrEdrConnectionRequest::CreateConnection(
CommandChannel* command_channel, async_dispatcher_t* dispatcher,
std::optional<uint16_t> clock_offset,
std::optional<PageScanRepetitionMode> page_scan_repetition_mode,
zx::duration timeout, OnCompleteDelegate on_command_fail) {
ZX_DEBUG_ASSERT(timeout > zx::msec(0));
// HCI Command Status Event will be sent as our completion callback.
auto self = weak_ptr_factory_.GetWeakPtr();
auto complete_cb = [self, timeout, peer_id = peer_id_,
on_command_fail = std::move(on_command_fail)](
auto, const EventPacket& event) {
ZX_DEBUG_ASSERT(event.event_code() == kCommandStatusEventCode);
if (!self)
return;
Status status = event.ToStatus();
if (!status) {
on_command_fail(status, peer_id);
} else {
// The request was started but has not completed; initiate the command
// timeout period. NOTE: The request will complete when the controller
// asynchronously notifies us of with a BrEdr Connection Complete event.
self->timeout_task_.PostDelayed(async_get_default_dispatcher(), timeout);
}
};
auto packet = CreateConnectionPacket(peer_address_, page_scan_repetition_mode,
clock_offset);
command_channel->SendCommand(std::move(packet), dispatcher,
std::move(complete_cb), kCommandStatusEventCode);
}
// Status is either a Success or an Error value
Status BrEdrConnectionRequest::CompleteRequest(Status status) {
bt_log(TRACE, "hci-bredr", "connection complete - status: %s",
bt_str(status));
timeout_task_.Cancel();
if (!status.is_success()) {
if (state_ == RequestState::kTimedOut) {
status = Status(HostError::kTimedOut);
} else if (status.protocol_error() == StatusCode::kUnknownConnectionId) {
// The "Unknown Connection Identifier" error code is returned if this
// event was sent due to a successful cancellation via the
// HCI_Create_Connection_Cancel command
// See Core Spec v5.0 Vol 2, Part E, Section 7.1.7
status = Status(HostError::kCanceled);
}
}
return status;
}
void BrEdrConnectionRequest::Timeout() {
// If the request was cancelled, this handler will have been removed
ZX_ASSERT(state_ == RequestState::kPending);
bt_log(INFO, "hci-bredr", "create connection timed out: canceling request");
state_ = RequestState::kTimedOut;
timeout_task_.Cancel();
}
bool BrEdrConnectionRequest::Cancel() {
if (!(state_ == RequestState::kPending)) {
bt_log(WARN, "hci-bredr", "connection attempt already canceled!");
return false;
}
state_ = RequestState::kCanceled;
timeout_task_.Cancel();
return true;
}
BrEdrConnectionRequest::~BrEdrConnectionRequest() { Cancel(); }
} // namespace hci
} // namespace bt