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/gap/low_energy_interrogator.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
19 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
20 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
21
22 namespace bt::gap {
23
LowEnergyInterrogator(Peer::WeakPtr peer,hci_spec::ConnectionHandle handle,hci::CommandChannel::WeakPtr cmd_channel,bool sca_supported)24 LowEnergyInterrogator::LowEnergyInterrogator(
25 Peer::WeakPtr peer,
26 hci_spec::ConnectionHandle handle,
27 hci::CommandChannel::WeakPtr cmd_channel,
28 bool sca_supported)
29 : peer_(std::move(peer)),
30 peer_id_(peer_->identifier()),
31 handle_(handle),
32 cmd_runner_(cmd_channel->AsWeakPtr()),
33 controller_supports_sca_(sca_supported),
34 weak_self_(this) {}
35
Start(ResultCallback callback)36 void LowEnergyInterrogator::Start(ResultCallback callback) {
37 PW_CHECK(!callback_);
38 callback_ = std::move(callback);
39
40 if (!peer_.is_alive()) {
41 Complete(ToResult(HostError::kFailed));
42 return;
43 }
44
45 PW_CHECK(peer_->le().has_value());
46
47 // Always read remote version information as a test of whether the connection
48 // was *actually* successfully established. If the connection failed to be
49 // established, the command status of the Read Remote Version Information
50 // command will be "Connection Failed to be Established". See
51 // fxbug.dev/42138706 for details.
52 QueueReadRemoteVersionInformation();
53
54 if (!peer_->le()->feature_interrogation_complete()) {
55 QueueReadLERemoteFeatures();
56 }
57
58 cmd_runner_.RunCommands([this](hci::Result<> result) {
59 // Accommodate unsupported remote feature interrogation (see
60 // http://b/361651988). In this case we know the peer doesn't support SCA,
61 // so we can return immediately.
62 if (peer_->le()->feature_interrogation_complete() &&
63 result == ToResult(pw::bluetooth::emboss::StatusCode::
64 UNSUPPORTED_REMOTE_FEATURE)) {
65 Complete(fit::ok());
66 return;
67 }
68
69 if (result.is_error() || !peer_->le()->features().has_value() ||
70 !controller_supports_sca_) {
71 Complete(result);
72 return;
73 }
74
75 // Verify the peer supports SCA updates
76 if (!(peer_->le()->features()->le_features &
77 static_cast<uint64_t>(
78 hci_spec::LESupportedFeature::kSleepClockAccuracyUpdates))) {
79 bt_log(INFO, "gap-le", "peer %s does not support SCA", bt_str(peer_id_));
80 Complete(result);
81 return;
82 }
83
84 QueueRequestPeerSca();
85 });
86 }
87
Cancel()88 void LowEnergyInterrogator::Cancel() {
89 if (!cmd_runner_.IsReady()) {
90 cmd_runner_.Cancel();
91 }
92 }
93
Complete(hci::Result<> result)94 void LowEnergyInterrogator::Complete(hci::Result<> result) {
95 if (!callback_) {
96 return;
97 }
98
99 auto self = weak_self_.GetWeakPtr();
100
101 // callback may destroy this object
102 callback_(result);
103
104 // Complete() may have been called by a command callback, in which case the
105 // runner needs to be canceled.
106 if (self.is_alive() && !cmd_runner_.IsReady()) {
107 cmd_runner_.Cancel();
108 }
109 }
110
QueueRequestPeerSca()111 void LowEnergyInterrogator::QueueRequestPeerSca() {
112 auto packet = hci::CommandPacket::New<
113 pw::bluetooth::emboss::LERequestPeerSCACommandWriter>(
114 hci_spec::kLERequestPeerSCA);
115 packet.view_t().connection_handle().Write(handle_);
116
117 // It's safe to capture |this| instead of a weak ptr to self because
118 // |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
119 // destroyed, and |this| outlives |cmd_runner_|.
120 auto cmd_cb = [this](const hci::EventPacket& event) {
121 if (HCI_IS_ERROR(event, WARN, "gap-le", "LE request peer SCA failed")) {
122 return;
123 }
124 auto view = event.view<
125 pw::bluetooth::emboss::LERequestPeerSCACompleteSubeventView>();
126 bt_log(DEBUG,
127 "gap-le",
128 "LE request peer SCA complete (peer: %s, value: %d)",
129 bt_str(peer_id_),
130 static_cast<uint8_t>(view.peer_clock_accuracy().Read()));
131 peer_->MutLe().set_sleep_clock_accuracy(view.peer_clock_accuracy().Read());
132 };
133
134 bt_log(TRACE, "gap-le", "requesting SCA for peer %s", bt_str(peer_id_));
135 cmd_runner_.QueueLeAsyncCommand(
136 std::move(packet),
137 hci_spec::kLERequestPeerSCACompleteSubeventCode,
138 std::move(cmd_cb),
139 /*wait=*/true);
140 cmd_runner_.RunCommands([this](hci::Result<> result) {
141 // This shouldn't happen since we verified that the peer supports SCA
142 // updates
143 PW_DCHECK(!result.is_error(),
144 "request for SCA from peer %s failed",
145 bt_str(peer_id_));
146 // Report success since the data is not critical and we don't want to
147 // interrupt pairing
148 Complete(fit::ok());
149 });
150 }
151
QueueReadLERemoteFeatures()152 void LowEnergyInterrogator::QueueReadLERemoteFeatures() {
153 auto packet = hci::CommandPacket::New<
154 pw::bluetooth::emboss::LEReadRemoteFeaturesCommandWriter>(
155 hci_spec::kLEReadRemoteFeatures);
156 packet.view_t().connection_handle().Write(handle_);
157
158 // It's safe to capture |this| instead of a weak ptr to self because
159 // |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
160 // destroyed, and |this| outlives |cmd_runner_|.
161 auto cmd_cb = [this](const hci::EventPacket& event) {
162 peer_->MutLe().SetFeatureInterrogationComplete();
163 if (HCI_IS_ERROR(event, WARN, "gap-le", "LE read remote features failed")) {
164 return;
165 }
166 bt_log(DEBUG,
167 "gap-le",
168 "LE read remote features complete (peer: %s)",
169 bt_str(peer_id_));
170 auto view = event.view<
171 pw::bluetooth::emboss::LEReadRemoteFeaturesCompleteSubeventView>();
172 peer_->MutLe().SetFeatures(hci_spec::LESupportedFeatures{
173 view.le_features().BackingStorage().ReadUInt()});
174 };
175
176 bt_log(TRACE,
177 "gap-le",
178 "sending LE read remote features command (peer id: %s)",
179 bt_str(peer_id_));
180 cmd_runner_.QueueLeAsyncCommand(
181 std::move(packet),
182 hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
183 std::move(cmd_cb),
184 /*wait=*/false);
185 }
186
QueueReadRemoteVersionInformation()187 void LowEnergyInterrogator::QueueReadRemoteVersionInformation() {
188 auto packet = hci::CommandPacket::New<
189 pw::bluetooth::emboss::ReadRemoteVersionInfoCommandWriter>(
190 hci_spec::kReadRemoteVersionInfo);
191 packet.view_t().connection_handle().Write(handle_);
192
193 // It's safe to capture |this| instead of a weak ptr to self because
194 // |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
195 // destroyed, and |this| outlives |cmd_runner_|.
196 auto cmd_cb = [this](const hci::EventPacket& event) {
197 if (HCI_IS_ERROR(
198 event, WARN, "gap-le", "read remote version info failed")) {
199 return;
200 }
201 PW_DCHECK(event.event_code() ==
202 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
203 bt_log(TRACE,
204 "gap-le",
205 "read remote version info completed (peer: %s)",
206 bt_str(peer_id_));
207 auto view = event.view<
208 pw::bluetooth::emboss::ReadRemoteVersionInfoCompleteEventView>();
209 peer_->set_version(view.version().Read(),
210 view.company_identifier().Read(),
211 view.subversion().Read());
212 };
213
214 bt_log(TRACE,
215 "gap-le",
216 "asking for version info (peer id: %s)",
217 bt_str(peer_id_));
218 cmd_runner_.QueueCommand(std::move(packet),
219 std::move(cmd_cb),
220 /*wait=*/false,
221 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
222 }
223
224 } // namespace bt::gap
225