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/bredr_interrogator.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/gap/peer.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
BrEdrInterrogator(Peer::WeakPtr peer,hci_spec::ConnectionHandle handle,hci::CommandChannel::WeakPtr cmd_channel)24 BrEdrInterrogator::BrEdrInterrogator(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_(std::move(cmd_channel)),
31 weak_self_(this) {
32 BT_ASSERT(peer_.is_alive());
33 }
34
Start(ResultCallback callback)35 void BrEdrInterrogator::Start(ResultCallback callback) {
36 callback_ = std::move(callback);
37
38 if (!peer_.is_alive() || !peer_->bredr()) {
39 Complete(ToResult(HostError::kFailed));
40 return;
41 }
42
43 if (!peer_->name()) {
44 QueueRemoteNameRequest();
45 }
46
47 if (!peer_->version()) {
48 QueueReadRemoteVersionInformation();
49 }
50
51 if (!peer_->features().HasPage(0)) {
52 QueueReadRemoteFeatures();
53 } else if (peer_->features().HasBit(
54 /*page=*/0, hci_spec::LMPFeature::kExtendedFeatures)) {
55 QueueReadRemoteExtendedFeatures(/*page=*/1);
56 }
57
58 if (!cmd_runner_.HasQueuedCommands()) {
59 Complete(fit::ok());
60 return;
61 }
62
63 cmd_runner_.RunCommands([this](hci::Result<> result) { Complete(result); });
64 }
65
Cancel()66 void BrEdrInterrogator::Cancel() {
67 if (!cmd_runner_.IsReady()) {
68 cmd_runner_.Cancel();
69 }
70 }
71
Complete(hci::Result<> result)72 void BrEdrInterrogator::Complete(hci::Result<> result) {
73 if (!callback_) {
74 return;
75 }
76
77 auto self = weak_self_.GetWeakPtr();
78
79 // callback may destroy this object
80 callback_(result);
81
82 if (self.is_alive() && !cmd_runner_.IsReady()) {
83 cmd_runner_.Cancel();
84 }
85 }
86
QueueRemoteNameRequest()87 void BrEdrInterrogator::QueueRemoteNameRequest() {
88 pw::bluetooth::emboss::PageScanRepetitionMode mode =
89 pw::bluetooth::emboss::PageScanRepetitionMode::R0_;
90 if (peer_->bredr()->page_scan_repetition_mode()) {
91 mode = *peer_->bredr()->page_scan_repetition_mode();
92 }
93
94 auto packet = hci::EmbossCommandPacket::New<
95 pw::bluetooth::emboss::RemoteNameRequestCommandWriter>(
96 hci_spec::kRemoteNameRequest);
97 auto params = packet.view_t();
98 params.bd_addr().CopyFrom(peer_->address().value().view());
99 params.page_scan_repetition_mode().Write(mode);
100 if (peer_->bredr()->clock_offset()) {
101 params.clock_offset().valid().Write(true);
102 const uint16_t offset = peer_->bredr()->clock_offset().value();
103 params.clock_offset().clock_offset().Write(offset);
104 }
105
106 auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
107 if (hci_is_error(event, WARN, "gap-bredr", "remote name request failed")) {
108 return;
109 }
110 bt_log(TRACE,
111 "gap-bredr",
112 "name request complete (peer id: %s)",
113 bt_str(peer_id_));
114
115 auto params =
116 event.view<pw::bluetooth::emboss::RemoteNameRequestCompleteEventView>();
117 emboss::support::ReadOnlyContiguousBuffer name =
118 params.remote_name().BackingStorage();
119 const unsigned char* name_end = std::find(name.begin(), name.end(), '\0');
120 std::string name_string(reinterpret_cast<const char*>(name.begin()),
121 reinterpret_cast<const char*>(name_end));
122 peer_->RegisterName(std::move(name_string),
123 Peer::NameSource::kNameDiscoveryProcedure);
124 };
125
126 bt_log(TRACE,
127 "gap-bredr",
128 "sending name request (peer id: %s)",
129 bt_str(peer_->identifier()));
130 cmd_runner_.QueueCommand(std::move(packet),
131 std::move(cmd_cb),
132 /*wait=*/false,
133 hci_spec::kRemoteNameRequestCompleteEventCode,
134 {hci_spec::kInquiry});
135 }
136
QueueReadRemoteFeatures()137 void BrEdrInterrogator::QueueReadRemoteFeatures() {
138 auto packet = hci::EmbossCommandPacket::New<
139 pw::bluetooth::emboss::ReadRemoteSupportedFeaturesCommandWriter>(
140 hci_spec::kReadRemoteSupportedFeatures);
141 packet.view_t().connection_handle().Write(handle_);
142
143 auto cmd_cb = [this](const hci::EventPacket& event) {
144 if (hci_is_error(event,
145 WARN,
146 "gap-bredr",
147 "read remote supported features failed")) {
148 return;
149 }
150 bt_log(TRACE,
151 "gap-bredr",
152 "remote features request complete (peer id: %s)",
153 bt_str(peer_id_));
154 const auto& params =
155 event.view()
156 .payload<
157 hci_spec::ReadRemoteSupportedFeaturesCompleteEventParams>();
158 peer_->SetFeaturePage(0, le64toh(params.lmp_features));
159
160 if (peer_->features().HasBit(/*page=*/0,
161 hci_spec::LMPFeature::kExtendedFeatures)) {
162 peer_->set_last_page_number(1);
163 QueueReadRemoteExtendedFeatures(/*page=*/1);
164 }
165 };
166
167 bt_log(TRACE,
168 "gap-bredr",
169 "asking for supported features (peer id: %s)",
170 bt_str(peer_id_));
171 cmd_runner_.QueueCommand(
172 std::move(packet),
173 std::move(cmd_cb),
174 /*wait=*/false,
175 hci_spec::kReadRemoteSupportedFeaturesCompleteEventCode);
176 }
177
QueueReadRemoteExtendedFeatures(uint8_t page)178 void BrEdrInterrogator::QueueReadRemoteExtendedFeatures(uint8_t page) {
179 auto packet = hci::EmbossCommandPacket::New<
180 pw::bluetooth::emboss::ReadRemoteExtendedFeaturesCommandWriter>(
181 hci_spec::kReadRemoteExtendedFeatures);
182 auto params = packet.view_t();
183 params.connection_handle().Write(handle_);
184 params.page_number().Write(page);
185
186 auto cmd_cb = [this, page](const hci::EmbossEventPacket& event) {
187 if (hci_is_error(event,
188 WARN,
189 "gap-bredr",
190 "read remote extended features failed (peer id: %s)",
191 bt_str(peer_id_))) {
192 return;
193 }
194 auto view = event.view<
195 pw::bluetooth::emboss::ReadRemoteExtendedFeaturesCompleteEventView>();
196
197 bt_log(TRACE,
198 "gap-bredr",
199 "got extended features page %u, max page %u (requested page: %u, "
200 "peer id: %s)",
201 view.page_number().Read(),
202 view.max_page_number().Read(),
203 page,
204 bt_str(peer_id_));
205
206 peer_->SetFeaturePage(view.page_number().Read(),
207 view.lmp_features().BackingStorage().ReadUInt());
208
209 if (view.page_number().Read() != page) {
210 bt_log(INFO,
211 "gap-bredr",
212 "requested page %u and got page %u, giving up (peer: %s)",
213 page,
214 view.page_number().Read(),
215 bt_str(peer_id_));
216 peer_->set_last_page_number(0);
217 return;
218 }
219
220 // NOTE: last page number will be capped at 2
221 peer_->set_last_page_number(view.max_page_number().Read());
222
223 if (page < peer_->features().last_page_number()) {
224 QueueReadRemoteExtendedFeatures(page + 1);
225 }
226 };
227
228 bt_log(TRACE,
229 "gap-bredr",
230 "requesting extended features page %u (peer id: %s)",
231 page,
232 bt_str(peer_id_));
233 cmd_runner_.QueueCommand(
234 std::move(packet),
235 std::move(cmd_cb),
236 /*wait=*/false,
237 hci_spec::kReadRemoteExtendedFeaturesCompleteEventCode);
238 }
239
QueueReadRemoteVersionInformation()240 void BrEdrInterrogator::QueueReadRemoteVersionInformation() {
241 auto packet = hci::EmbossCommandPacket::New<
242 pw::bluetooth::emboss::ReadRemoteVersionInfoCommandWriter>(
243 hci_spec::kReadRemoteVersionInfo);
244 packet.view_t().connection_handle().Write(handle_);
245
246 auto cmd_cb = [this](const hci::EmbossEventPacket& event) {
247 if (hci_is_error(event, WARN, "gap", "read remote version info failed")) {
248 return;
249 }
250 BT_DEBUG_ASSERT(event.event_code() ==
251 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
252 bt_log(TRACE,
253 "gap",
254 "read remote version info completed (peer id: %s)",
255 bt_str(peer_id_));
256 auto view = event.view<
257 pw::bluetooth::emboss::ReadRemoteVersionInfoCompleteEventView>();
258 peer_->set_version(view.version().Read(),
259 view.company_identifier().Read(),
260 view.subversion().Read());
261 };
262
263 bt_log(
264 TRACE, "gap", "asking for version info (peer id: %s)", bt_str(peer_id_));
265 cmd_runner_.QueueCommand(std::move(packet),
266 std::move(cmd_cb),
267 /*wait=*/false,
268 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
269 }
270
271 } // namespace bt::gap
272