1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection_request.h"
16
17 #include <pw_assert/check.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
21
22 namespace bt::hci {
23
CreateConnectionPacket(DeviceAddress address,std::optional<pw::bluetooth::emboss::PageScanRepetitionMode> page_scan_repetition_mode,std::optional<uint16_t> clock_offset)24 CommandPacket CreateConnectionPacket(
25 DeviceAddress address,
26 std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
27 page_scan_repetition_mode,
28 std::optional<uint16_t> clock_offset) {
29 auto request =
30 CommandPacket::New<pw::bluetooth::emboss::CreateConnectionCommandWriter>(
31 hci_spec::kCreateConnection);
32 auto params = request.view_t();
33 params.bd_addr().CopyFrom(address.value().view());
34 params.packet_type().BackingStorage().WriteUInt(kEnableAllPacketTypes);
35
36 // The Page Scan Repetition Mode of the remote device as retrieved by Inquiry.
37 // If we do not have one for the device, opt for R2 so we will send for at
38 // least 2.56s
39 if (page_scan_repetition_mode) {
40 params.page_scan_repetition_mode().Write(*page_scan_repetition_mode);
41 } else {
42 params.page_scan_repetition_mode().Write(
43 pw::bluetooth::emboss::PageScanRepetitionMode::R2_);
44 }
45
46 params.reserved().Write(0); // Reserved, must be set to 0.
47
48 // Clock Offset. The lower 15 bits are set to the clock offset as retrieved
49 // by an Inquiry. The highest bit is set to 1 if the rest of this parameter
50 // is valid. If we don't have one, use the default.
51 if (clock_offset) {
52 params.clock_offset().valid().Write(true);
53 params.clock_offset().clock_offset().Write(*clock_offset);
54 } else {
55 params.clock_offset().valid().Write(false);
56 }
57
58 params.allow_role_switch().Write(
59 pw::bluetooth::emboss::GenericEnableParam::DISABLE);
60
61 return request;
62 }
63
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)64 void BrEdrConnectionRequest::CreateConnection(
65 CommandChannel* command_channel,
66 std::optional<uint16_t> clock_offset,
67 std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
68 page_scan_repetition_mode,
69 pw::chrono::SystemClock::duration timeout,
70 OnCompleteDelegate on_command_fail) {
71 PW_DCHECK(timeout.count() > 0);
72
73 // HCI Command Status Event will be sent as our completion callback.
74 auto self = weak_self_.GetWeakPtr();
75 auto complete_cb = [self,
76 timeout,
77 peer_id = peer_id_,
78 on_command_fail_cb = std::move(on_command_fail)](
79 auto, const EventPacket& event) {
80 PW_DCHECK(event.event_code() == hci_spec::kCommandStatusEventCode);
81
82 if (!self.is_alive())
83 return;
84
85 Result<> status = event.ToResult();
86 if (status.is_error()) {
87 on_command_fail_cb(status, peer_id);
88 } else {
89 // Both CommandChannel and the controller perform some scheduling, so log
90 // when the controller finally acknowledges Create Connection to observe
91 // outgoing connection sequencing.
92 // TODO(fxbug.dev/42173957): Added to investigate timing and can be
93 // removed if it adds no value
94 bt_log(INFO,
95 "hci-bredr",
96 "Create Connection for peer %s successfully dispatched",
97 bt_str(peer_id));
98
99 // The request was started but has not completed; initiate the command
100 // timeout period. NOTE: The request will complete when the controller
101 // asynchronously notifies us of with a BrEdr Connection Complete event.
102 self->timeout_task_.PostAfter(timeout);
103 }
104 };
105
106 auto packet = CreateConnectionPacket(
107 peer_address_, page_scan_repetition_mode, clock_offset);
108
109 bt_log(INFO,
110 "hci-bredr",
111 "initiating connection request (peer: %s)",
112 bt_str(peer_id_));
113 command_channel->SendCommand(std::move(packet),
114 std::move(complete_cb),
115 hci_spec::kCommandStatusEventCode);
116 }
117
118 // Status is either a Success or an Error value
CompleteRequest(Result<> status)119 Result<> BrEdrConnectionRequest::CompleteRequest(Result<> status) {
120 bt_log(INFO,
121 "hci-bredr",
122 "connection complete (status: %s, peer: %s)",
123 bt_str(status),
124 bt_str(peer_id_));
125 timeout_task_.Cancel();
126
127 if (status.is_error()) {
128 if (state_ == RequestState::kTimedOut) {
129 return ToResult(HostError::kTimedOut);
130 }
131 if (status ==
132 ToResult(pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID)) {
133 // The "Unknown Connection Identifier" error code is returned if this
134 // event was sent due to a successful cancellation via the
135 // HCI_Create_Connection_Cancel command
136 // See Core Spec v5.0 Vol 2, Part E, Section 7.1.7
137 state_ = RequestState::kCanceled;
138 return ToResult(HostError::kCanceled);
139 }
140 }
141 state_ = RequestState::kSuccess;
142 return status;
143 }
144
Timeout()145 void BrEdrConnectionRequest::Timeout() {
146 // If the request was cancelled, this handler will have been removed
147 PW_CHECK(state_ == RequestState::kPending);
148 bt_log(INFO,
149 "hci-bredr",
150 "create connection timed out: canceling request (peer: %s)",
151 bt_str(peer_id_));
152 state_ = RequestState::kTimedOut;
153 timeout_task_.Cancel();
154 }
155
Cancel()156 bool BrEdrConnectionRequest::Cancel() {
157 if (state_ == RequestState::kSuccess) {
158 bt_log(DEBUG,
159 "hci-bredr",
160 "connection has already succeeded (peer: %s)",
161 bt_str(peer_id_));
162 return false;
163 }
164 if (state_ != RequestState::kPending) {
165 bt_log(WARN,
166 "hci-bredr",
167 "connection attempt already canceled! (peer: %s)",
168 bt_str(peer_id_));
169 return false;
170 }
171 // TODO(fxbug.dev/42143836) - We should correctly handle cancels due to a
172 // disconnect call during a pending connection creation attempt
173 bt_log(INFO,
174 "hci-bredr",
175 "canceling connection request (peer: %s)",
176 bt_str(peer_id_));
177 state_ = RequestState::kCanceled;
178 timeout_task_.Cancel();
179 return true;
180 }
181
~BrEdrConnectionRequest()182 BrEdrConnectionRequest::~BrEdrConnectionRequest() { Cancel(); }
183
184 } // namespace bt::hci
185