xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gap/low_energy_interrogator.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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