• 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/hci/sequential_command_runner.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
18 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
19 
20 #pragma clang diagnostic ignored "-Wshadow"
21 
22 namespace bt::hci {
23 
SequentialCommandRunner(hci::CommandChannel::WeakPtr cmd_channel)24 SequentialCommandRunner::SequentialCommandRunner(
25     hci::CommandChannel::WeakPtr cmd_channel)
26     : cmd_(std::move(cmd_channel)),
27       sequence_number_(0u),
28       running_commands_(0u),
29       weak_ptr_factory_(this) {
30   BT_DEBUG_ASSERT(cmd_.is_alive());
31 }
32 
QueueCommand(CommandPacketVariant command_packet,CommandCompleteCallbackVariant callback,bool wait,hci_spec::EventCode complete_event_code,std::unordered_set<hci_spec::OpCode> exclusions)33 void SequentialCommandRunner::QueueCommand(
34     CommandPacketVariant command_packet,
35     CommandCompleteCallbackVariant callback,
36     bool wait,
37     hci_spec::EventCode complete_event_code,
38     std::unordered_set<hci_spec::OpCode> exclusions) {
39   if (std::holds_alternative<std::unique_ptr<CommandPacket>>(command_packet)) {
40     BT_DEBUG_ASSERT(sizeof(hci_spec::CommandHeader) <=
41                     std::get<std::unique_ptr<CommandPacket>>(command_packet)
42                         ->view()
43                         .size());
44   }
45 
46   command_queue_.emplace(
47       QueuedCommand{.packet = std::move(command_packet),
48                     .complete_event_code = complete_event_code,
49                     .is_le_async_command = false,
50                     .callback = std::move(callback),
51                     .wait = wait,
52                     .exclusions = std::move(exclusions)});
53 
54   if (status_callback_) {
55     TryRunNextQueuedCommand();
56   }
57 }
58 
QueueLeAsyncCommand(CommandPacketVariant command_packet,hci_spec::EventCode le_meta_subevent_code,CommandCompleteCallbackVariant callback,bool wait)59 void SequentialCommandRunner::QueueLeAsyncCommand(
60     CommandPacketVariant command_packet,
61     hci_spec::EventCode le_meta_subevent_code,
62     CommandCompleteCallbackVariant callback,
63     bool wait) {
64   command_queue_.emplace(
65       QueuedCommand{.packet = std::move(command_packet),
66                     .complete_event_code = le_meta_subevent_code,
67                     .is_le_async_command = true,
68                     .callback = std::move(callback),
69                     .wait = wait,
70                     .exclusions = {}});
71 
72   if (status_callback_) {
73     TryRunNextQueuedCommand();
74   }
75 }
76 
RunCommands(ResultFunction<> status_callback)77 void SequentialCommandRunner::RunCommands(ResultFunction<> status_callback) {
78   BT_DEBUG_ASSERT(!status_callback_);
79   BT_DEBUG_ASSERT(status_callback);
80   BT_DEBUG_ASSERT(!command_queue_.empty());
81 
82   status_callback_ = std::move(status_callback);
83   sequence_number_++;
84 
85   TryRunNextQueuedCommand();
86 }
87 
IsReady() const88 bool SequentialCommandRunner::IsReady() const { return !status_callback_; }
89 
Cancel()90 void SequentialCommandRunner::Cancel() {
91   NotifyStatusAndReset(ToResult(HostError::kCanceled));
92 }
93 
HasQueuedCommands() const94 bool SequentialCommandRunner::HasQueuedCommands() const {
95   return !command_queue_.empty();
96 }
97 
TryRunNextQueuedCommand(Result<> status)98 void SequentialCommandRunner::TryRunNextQueuedCommand(Result<> status) {
99   BT_DEBUG_ASSERT(status_callback_);
100 
101   // If an error occurred or we're done, reset.
102   if (status.is_error() || (command_queue_.empty() && running_commands_ == 0)) {
103     NotifyStatusAndReset(status);
104     return;
105   }
106 
107   // Wait for the rest of the running commands to finish if we need to.
108   if (command_queue_.empty() ||
109       (running_commands_ > 0 && command_queue_.front().wait)) {
110     return;
111   }
112 
113   QueuedCommand next = std::move(command_queue_.front());
114   command_queue_.pop();
115 
116   auto self = weak_ptr_factory_.GetWeakPtr();
117   auto command_callback = [self,
118                            cmd_cb = std::move(next.callback),
119                            complete_event_code = next.complete_event_code,
120                            seq_no = sequence_number_](
121                               auto, const EventPacket& event_packet) {
122     std::optional<EmbossEventPacket> emboss_packet;
123     hci::Result<> status =
124         bt::ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS);
125     using T = std::decay_t<decltype(cmd_cb)>;
126     if constexpr (std::is_same_v<T, CommandCompleteCallback>) {
127       status = event_packet.ToResult();
128     } else {
129       emboss_packet = EmbossEventPacket::New(event_packet.view().size());
130       MutableBufferView buffer = emboss_packet->mutable_data();
131       event_packet.view().data().Copy(&buffer);
132       status = emboss_packet->ToResult();
133     }
134 
135     if (self.is_alive() && seq_no != self->sequence_number_) {
136       bt_log(TRACE,
137              "hci",
138              "Ignoring event for previous sequence (event code: %#.2x, status: "
139              "%s)",
140              event_packet.event_code(),
141              bt_str(status));
142     }
143 
144     // The sequence could have failed or been canceled, and a new sequence could
145     // have started. This check prevents calling cmd_cb if the corresponding
146     // sequence is no longer running.
147     if (!self.is_alive() || !self->status_callback_ ||
148         seq_no != self->sequence_number_) {
149       return;
150     }
151 
152     if (status.is_ok() &&
153         event_packet.event_code() == hci_spec::kCommandStatusEventCode &&
154         complete_event_code != hci_spec::kCommandStatusEventCode) {
155       return;
156     }
157 
158     std::visit(
159         [&event_packet, &emboss_packet](auto& cmd_cb) {
160           using T = std::decay_t<decltype(cmd_cb)>;
161           if constexpr (std::is_same_v<T, CommandCompleteCallback>) {
162             if (cmd_cb) {
163               cmd_cb(event_packet);
164             }
165           } else if constexpr (std::is_same_v<T,
166                                               EmbossCommandCompleteCallback>) {
167             if (cmd_cb) {
168               cmd_cb(*emboss_packet);
169             }
170           }
171         },
172         cmd_cb);
173 
174     // The callback could have destroyed, canceled, or restarted the command
175     // runner.  While this check looks redundant to the above check, the state
176     // could have changed in cmd_cb.
177     if (!self.is_alive() || !self->status_callback_ ||
178         seq_no != self->sequence_number_) {
179       return;
180     }
181 
182     BT_DEBUG_ASSERT(self->running_commands_ > 0);
183     self->running_commands_--;
184     self->TryRunNextQueuedCommand(status);
185   };
186 
187   running_commands_++;
188   if (!SendQueuedCommand(std::move(next), std::move(command_callback))) {
189     NotifyStatusAndReset(ToResult(HostError::kFailed));
190   } else {
191     TryRunNextQueuedCommand();
192   }
193 }
194 
SendQueuedCommand(QueuedCommand command,CommandChannel::CommandCallback callback)195 bool SequentialCommandRunner::SendQueuedCommand(
196     QueuedCommand command, CommandChannel::CommandCallback callback) {
197   if (!cmd_.is_alive()) {
198     bt_log(
199         INFO, "hci", "SequentialCommandRunner command channel died, aborting");
200     return false;
201   }
202   if (command.is_le_async_command) {
203     return cmd_->SendLeAsyncCommand(std::move(command.packet),
204                                     std::move(callback),
205                                     command.complete_event_code);
206   }
207 
208   return cmd_->SendExclusiveCommand(std::move(command.packet),
209                                     std::move(callback),
210                                     command.complete_event_code,
211                                     std::move(command.exclusions));
212 }
213 
Reset()214 void SequentialCommandRunner::Reset() {
215   if (!command_queue_.empty()) {
216     command_queue_ = {};
217   }
218   running_commands_ = 0;
219   status_callback_ = nullptr;
220 }
221 
NotifyStatusAndReset(Result<> status)222 void SequentialCommandRunner::NotifyStatusAndReset(Result<> status) {
223   BT_DEBUG_ASSERT(status_callback_);
224   auto status_cb = std::move(status_callback_);
225   Reset();
226   status_cb(status);
227 }
228 
229 }  // namespace bt::hci
230