blob: 9f0227adf927c4db6f3e1fa3b3db2750cf54f252 [file] [log] [blame]
// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection_request.h"
#include "pw_bluetooth_sapphire/internal/host/common/log.h"
#include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
namespace bt::hci {
EmbossCommandPacket CreateConnectionPacket(
DeviceAddress address,
std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
page_scan_repetition_mode,
std::optional<uint16_t> clock_offset) {
auto request = EmbossCommandPacket::New<
pw::bluetooth::emboss::CreateConnectionCommandWriter>(
hci_spec::kCreateConnection);
auto params = request.view_t();
params.bd_addr().CopyFrom(address.value().view());
params.packet_type().BackingStorage().WriteUInt(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().Write(*page_scan_repetition_mode);
} else {
params.page_scan_repetition_mode().Write(
pw::bluetooth::emboss::PageScanRepetitionMode::R2_);
}
params.reserved().Write(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().valid().Write(true);
params.clock_offset().clock_offset().Write(*clock_offset);
} else {
params.clock_offset().valid().Write(false);
}
params.allow_role_switch().Write(
pw::bluetooth::emboss::GenericEnableParam::DISABLE);
return request;
}
void BrEdrConnectionRequest::CreateConnection(
CommandChannel* command_channel,
std::optional<uint16_t> clock_offset,
std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
page_scan_repetition_mode,
pw::chrono::SystemClock::duration timeout,
OnCompleteDelegate on_command_fail) {
BT_DEBUG_ASSERT(timeout.count() > 0);
// HCI Command Status Event will be sent as our completion callback.
auto self = weak_self_.GetWeakPtr();
auto complete_cb = [self,
timeout,
peer_id = peer_id_,
on_command_fail = std::move(on_command_fail)](
auto, const EventPacket& event) {
BT_DEBUG_ASSERT(event.event_code() == hci_spec::kCommandStatusEventCode);
if (!self.is_alive())
return;
Result<> status = event.ToResult();
if (status.is_error()) {
on_command_fail(status, peer_id);
} else {
// Both CommandChannel and the controller perform some scheduling, so log
// when the controller finally acknowledges Create Connection to observe
// outgoing connection sequencing.
// TODO(fxbug.dev/42173957): Added to investigate timing and can be
// removed if it adds no value
bt_log(INFO,
"hci-bredr",
"Create Connection for peer %s successfully dispatched",
bt_str(peer_id));
// 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_.PostAfter(timeout);
}
};
auto packet = CreateConnectionPacket(
peer_address_, page_scan_repetition_mode, clock_offset);
bt_log(INFO,
"hci-bredr",
"initiating connection request (peer: %s)",
bt_str(peer_id_));
command_channel->SendCommand(std::move(packet),
std::move(complete_cb),
hci_spec::kCommandStatusEventCode);
}
// Status is either a Success or an Error value
Result<> BrEdrConnectionRequest::CompleteRequest(Result<> status) {
bt_log(INFO,
"hci-bredr",
"connection complete (status: %s, peer: %s)",
bt_str(status),
bt_str(peer_id_));
timeout_task_.Cancel();
if (status.is_error()) {
if (state_ == RequestState::kTimedOut) {
return ToResult(HostError::kTimedOut);
}
if (status ==
ToResult(pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID)) {
// 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
state_ = RequestState::kCanceled;
return ToResult(HostError::kCanceled);
}
}
state_ = RequestState::kSuccess;
return status;
}
void BrEdrConnectionRequest::Timeout() {
// If the request was cancelled, this handler will have been removed
BT_ASSERT(state_ == RequestState::kPending);
bt_log(INFO,
"hci-bredr",
"create connection timed out: canceling request (peer: %s)",
bt_str(peer_id_));
state_ = RequestState::kTimedOut;
timeout_task_.Cancel();
}
bool BrEdrConnectionRequest::Cancel() {
if (state_ == RequestState::kSuccess) {
bt_log(DEBUG,
"hci-bredr",
"connection has already succeeded (peer: %s)",
bt_str(peer_id_));
return false;
}
if (state_ != RequestState::kPending) {
bt_log(WARN,
"hci-bredr",
"connection attempt already canceled! (peer: %s)",
bt_str(peer_id_));
return false;
}
// TODO(fxbug.dev/42143836) - We should correctly handle cancels due to a
// disconnect call during a pending connection creation attempt
bt_log(INFO,
"hci-bredr",
"canceling connection request (peer: %s)",
bt_str(peer_id_));
state_ = RequestState::kCanceled;
timeout_task_.Cancel();
return true;
}
BrEdrConnectionRequest::~BrEdrConnectionRequest() { Cancel(); }
} // namespace bt::hci