• 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_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