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_bytes/endian.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/gap/peer.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
BrEdrInterrogator(Peer::WeakPtr peer,hci_spec::ConnectionHandle handle,hci::CommandChannel::WeakPtr cmd_channel)26 BrEdrInterrogator::BrEdrInterrogator(Peer::WeakPtr peer,
27 hci_spec::ConnectionHandle handle,
28 hci::CommandChannel::WeakPtr cmd_channel)
29 : peer_(std::move(peer)),
30 peer_id_(peer_->identifier()),
31 handle_(handle),
32 cmd_runner_(std::move(cmd_channel)),
33 weak_self_(this) {
34 PW_CHECK(peer_.is_alive());
35 }
36
Start(ResultCallback callback)37 void BrEdrInterrogator::Start(ResultCallback callback) {
38 callback_ = std::move(callback);
39
40 if (!peer_.is_alive() || !peer_->bredr()) {
41 Complete(ToResult(HostError::kFailed));
42 return;
43 }
44
45 if (!peer_->name()) {
46 QueueRemoteNameRequest();
47 }
48
49 if (!peer_->version()) {
50 QueueReadRemoteVersionInformation();
51 }
52
53 if (!peer_->features().HasPage(0)) {
54 QueueReadRemoteFeatures();
55 } else if (peer_->features().HasBit(
56 /*page=*/0, hci_spec::LMPFeature::kExtendedFeatures)) {
57 QueueReadRemoteExtendedFeatures(/*page=*/1);
58 }
59
60 if (!cmd_runner_.HasQueuedCommands()) {
61 Complete(fit::ok());
62 return;
63 }
64
65 cmd_runner_.RunCommands([this](hci::Result<> result) { Complete(result); });
66 }
67
Cancel()68 void BrEdrInterrogator::Cancel() {
69 if (!cmd_runner_.IsReady()) {
70 cmd_runner_.Cancel();
71 }
72 }
73
Complete(hci::Result<> result)74 void BrEdrInterrogator::Complete(hci::Result<> result) {
75 if (!callback_) {
76 return;
77 }
78
79 auto self = weak_self_.GetWeakPtr();
80
81 // callback may destroy this object
82 callback_(result);
83
84 if (self.is_alive() && !cmd_runner_.IsReady()) {
85 cmd_runner_.Cancel();
86 }
87 }
88
QueueRemoteNameRequest()89 void BrEdrInterrogator::QueueRemoteNameRequest() {
90 pw::bluetooth::emboss::PageScanRepetitionMode mode =
91 pw::bluetooth::emboss::PageScanRepetitionMode::R0_;
92 if (peer_->bredr()->page_scan_repetition_mode()) {
93 mode = *peer_->bredr()->page_scan_repetition_mode();
94 }
95
96 auto packet = hci::CommandPacket::New<
97 pw::bluetooth::emboss::RemoteNameRequestCommandWriter>(
98 hci_spec::kRemoteNameRequest);
99 auto packet_view = packet.view_t();
100 packet_view.bd_addr().CopyFrom(peer_->address().value().view());
101 packet_view.page_scan_repetition_mode().Write(mode);
102 if (peer_->bredr()->clock_offset()) {
103 packet_view.clock_offset().valid().Write(true);
104 const uint16_t offset = peer_->bredr()->clock_offset().value();
105 packet_view.clock_offset().clock_offset().Write(offset);
106 }
107
108 auto cmd_cb = [this](const hci::EventPacket& event) {
109 if (HCI_IS_ERROR(event, WARN, "gap-bredr", "remote name request failed")) {
110 return;
111 }
112 bt_log(TRACE,
113 "gap-bredr",
114 "name request complete (peer id: %s)",
115 bt_str(peer_id_));
116
117 auto params =
118 event.view<pw::bluetooth::emboss::RemoteNameRequestCompleteEventView>();
119 emboss::support::ReadOnlyContiguousBuffer name =
120 params.remote_name().BackingStorage();
121 const unsigned char* name_end = std::find(name.begin(), name.end(), '\0');
122 std::string name_string(reinterpret_cast<const char*>(name.begin()),
123 reinterpret_cast<const char*>(name_end));
124 peer_->RegisterName(std::move(name_string),
125 Peer::NameSource::kNameDiscoveryProcedure);
126 };
127
128 bt_log(TRACE,
129 "gap-bredr",
130 "sending name request (peer id: %s)",
131 bt_str(peer_->identifier()));
132 cmd_runner_.QueueCommand(std::move(packet),
133 std::move(cmd_cb),
134 /*wait=*/false,
135 hci_spec::kRemoteNameRequestCompleteEventCode,
136 {hci_spec::kInquiry});
137 }
138
QueueReadRemoteFeatures()139 void BrEdrInterrogator::QueueReadRemoteFeatures() {
140 auto packet = hci::CommandPacket::New<
141 pw::bluetooth::emboss::ReadRemoteSupportedFeaturesCommandWriter>(
142 hci_spec::kReadRemoteSupportedFeatures);
143 packet.view_t().connection_handle().Write(handle_);
144
145 auto cmd_cb = [this](const hci::EventPacket& event) {
146 if (HCI_IS_ERROR(event,
147 WARN,
148 "gap-bredr",
149 "read remote supported features failed")) {
150 return;
151 }
152 bt_log(TRACE,
153 "gap-bredr",
154 "remote features request complete (peer id: %s)",
155 bt_str(peer_id_));
156 auto view = event.view<
157 pw::bluetooth::emboss::ReadRemoteSupportedFeaturesCompleteEventView>();
158 peer_->SetFeaturePage(0, view.lmp_features().BackingStorage().ReadUInt());
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::CommandPacket::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::EventPacket& 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::CommandPacket::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::EventPacket& event) {
247 if (HCI_IS_ERROR(event, WARN, "gap", "read remote version info failed")) {
248 return;
249 }
250 PW_DCHECK(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