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