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