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/protocol.h"
19 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
20
21 namespace bt::gap {
22
LowEnergyInterrogator(Peer::WeakPtr peer,hci_spec::ConnectionHandle handle,hci::CommandChannel::WeakPtr cmd_channel)23 LowEnergyInterrogator::LowEnergyInterrogator(
24 Peer::WeakPtr peer,
25 hci_spec::ConnectionHandle handle,
26 hci::CommandChannel::WeakPtr cmd_channel)
27 : peer_(std::move(peer)),
28 peer_id_(peer_->identifier()),
29 handle_(handle),
30 cmd_runner_(cmd_channel->AsWeakPtr()),
31 weak_self_(this) {}
32
Start(ResultCallback callback)33 void LowEnergyInterrogator::Start(ResultCallback callback) {
34 BT_ASSERT(!callback_);
35 callback_ = std::move(callback);
36
37 if (!peer_.is_alive()) {
38 Complete(ToResult(HostError::kFailed));
39 return;
40 }
41
42 BT_ASSERT(peer_->le().has_value());
43
44 // Always read remote version information as a test of whether the connection
45 // was *actually* successfully established. If the connection failed to be
46 // established, the command status of the Read Remote Version Information
47 // command will be "Connection Failed to be Established". See
48 // fxbug.dev/42138706 for details.
49 QueueReadRemoteVersionInformation();
50
51 if (!peer_->le()->features().has_value()) {
52 QueueReadLERemoteFeatures();
53 }
54
55 cmd_runner_.RunCommands([this](hci::Result<> result) { Complete(result); });
56 }
57
Cancel()58 void LowEnergyInterrogator::Cancel() {
59 if (!cmd_runner_.IsReady()) {
60 cmd_runner_.Cancel();
61 }
62 }
63
Complete(hci::Result<> result)64 void LowEnergyInterrogator::Complete(hci::Result<> result) {
65 if (!callback_) {
66 return;
67 }
68
69 auto self = weak_self_.GetWeakPtr();
70
71 // callback may destroy this object
72 callback_(result);
73
74 // Complete() may have been called by a command callback, in which case the
75 // runner needs to be canceled.
76 if (self.is_alive() && !cmd_runner_.IsReady()) {
77 cmd_runner_.Cancel();
78 }
79 }
80
QueueReadLERemoteFeatures()81 void LowEnergyInterrogator::QueueReadLERemoteFeatures() {
82 auto packet = hci::EmbossCommandPacket::New<
83 pw::bluetooth::emboss::LEReadRemoteFeaturesCommandWriter>(
84 hci_spec::kLEReadRemoteFeatures);
85 packet.view_t().connection_handle().Write(handle_);
86
87 // It's safe to capture |this| instead of a weak ptr to self because
88 // |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
89 // destroyed, and |this| outlives |cmd_runner_|.
90 auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
91 if (hci_is_error(event, WARN, "gap-le", "LE read remote features failed")) {
92 return;
93 }
94 bt_log(DEBUG,
95 "gap-le",
96 "LE read remote features complete (peer: %s)",
97 bt_str(peer_id_));
98 auto view = event.view<
99 pw::bluetooth::emboss::LEReadRemoteFeaturesCompleteSubeventView>();
100 peer_->MutLe().SetFeatures(hci_spec::LESupportedFeatures{
101 view.le_features().BackingStorage().ReadUInt()});
102 };
103
104 bt_log(TRACE,
105 "gap-le",
106 "sending LE read remote features command (peer id: %s)",
107 bt_str(peer_id_));
108 cmd_runner_.QueueLeAsyncCommand(
109 std::move(packet),
110 hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
111 std::move(cmd_cb),
112 /*wait=*/false);
113 }
114
QueueReadRemoteVersionInformation()115 void LowEnergyInterrogator::QueueReadRemoteVersionInformation() {
116 auto packet = hci::EmbossCommandPacket::New<
117 pw::bluetooth::emboss::ReadRemoteVersionInfoCommandWriter>(
118 hci_spec::kReadRemoteVersionInfo);
119 packet.view_t().connection_handle().Write(handle_);
120
121 // It's safe to capture |this| instead of a weak ptr to self because
122 // |cmd_runner_| guarantees that |cmd_cb| won't be invoked if |cmd_runner_| is
123 // destroyed, and |this| outlives |cmd_runner_|.
124 auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
125 if (hci_is_error(
126 event, WARN, "gap-le", "read remote version info failed")) {
127 return;
128 }
129 BT_DEBUG_ASSERT(event.event_code() ==
130 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
131 bt_log(TRACE,
132 "gap-le",
133 "read remote version info completed (peer: %s)",
134 bt_str(peer_id_));
135 auto view = event.view<
136 pw::bluetooth::emboss::ReadRemoteVersionInfoCompleteEventView>();
137 peer_->set_version(view.version().Read(),
138 view.company_identifier().Read(),
139 view.subversion().Read());
140 };
141
142 bt_log(TRACE,
143 "gap-le",
144 "asking for version info (peer id: %s)",
145 bt_str(peer_id_));
146 cmd_runner_.QueueCommand(std::move(packet),
147 std::move(cmd_cb),
148 /*wait=*/false,
149 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
150 }
151
152 } // namespace bt::gap
153