• 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 #pragma once
16 #include <queue>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
19 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
20 #include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
21 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
22 
23 namespace bt::hci {
24 
25 class Transport;
26 
27 // A SequentialCommandRunner can be used to chain HCI commands such that
28 // commands in the sequence are sent to the controller only after previous
29 // commands have completed successfully.
30 //
31 // When a command fails due to an error status (in a HCI_Command_Status or
32 // HCI_Command_Complete event), the rest of the sequence is abandoned and an
33 // error status is reported back to the caller. Already sent commands will
34 // continue but not report their statuses to the caller.
35 //
36 // Commands are always sent in the order that they are queued.  If any command
37 // fails, unsent commands are abandoned.
38 //
39 // Parts of the sequence can be run in parallel by using the |wait| parameter
40 // of QueueCommand.  If |wait| is true, all commands queued before that command
41 // must complete (and succeed) before the command will be sent.
42 //
43 // For example:
44 // QueueCommand(a, [](const auto&){}, true);
45 // QueueCommand(b, [](const auto&){}, false);
46 // QueueCommand(c, [](const auto&){}, false);
47 // QueueCommand(d, [](const auto&){}, true);
48 // QueueCommand(e, [](const auto&){}, false);
49 // RunCommands([](auto){});
50 //
51 // Command a, b, and c will be run simultaneously.  If any failed, then
52 // RunCommands will report that error back to the caller.  All three of a, b,
53 // and c's callbacks are run.  If all three succeeded, then d and e will run
54 // simultaneously, and when both complete, the whole sequence is complete and
55 // RunCommands will report success.
56 class SequentialCommandRunner final {
57  public:
58   using CommandPacketVariant = CommandChannel::CommandPacketVariant;
59 
60   explicit SequentialCommandRunner(hci::CommandChannel::WeakPtr cmd_channel);
61   ~SequentialCommandRunner() = default;
62 
63   // Adds a HCI command packet to the queue.
64   // If |callback| is provided, it will run with the event that completes the
65   // command.
66   // If |wait| is true, then all previously queued commands must complete
67   // successfully before this command is sent.
68   // |exclusions| will be passed to CommandChannel::SendExclusiveCommand().
69   using CommandCompleteCallback = fit::function<void(const EventPacket& event)>;
70   using EmbossCommandCompleteCallback =
71       fit::function<void(const EmbossEventPacket& event_packet)>;
72   using CommandCompleteCallbackVariant =
73       std::variant<CommandCompleteCallback, EmbossCommandCompleteCallback>;
74   void QueueCommand(
75       CommandPacketVariant command_packet,
76       CommandCompleteCallbackVariant callback = CommandCompleteCallback(),
77       bool wait = true,
78       hci_spec::EventCode complete_event_code =
79           hci_spec::kCommandCompleteEventCode,
80       std::unordered_set<hci_spec::OpCode> exclusions = {});
81 
82   // Same as QueueCommand(), except the command completes on the LE Meta Event
83   // with subevent code |le_meta_subevent_code|.
84   void QueueLeAsyncCommand(
85       CommandPacketVariant command_packet,
86       hci_spec::EventCode le_meta_subevent_code,
87       CommandCompleteCallbackVariant callback = CommandCompleteCallback(),
88       bool wait = true);
89 
90   // Runs all the queued commands. This method will return before queued
91   // commands have been run. |status_callback| is called with the status of the
92   // last command run, or kSuccess if all commands returned
93   // HCI_Command_Complete.
94   //
95   // Once RunCommands() has been called this instance will not be ready for
96   // re-use until |status_callback| gets run. At that point new commands can be
97   // queued and run (see IsReady()).
98   //
99   // Commands can continue to be queued while commands are being run, including
100   // from inside of command callbacks.
101   //
102   // RunCommands() will always send at least one HCI command to CommandChannel
103   // if any are queued, which can not be prevented by a call to Cancel().
104   void RunCommands(ResultFunction<> status_callback);
105 
106   // Returns true if commands are not running on this instance. This
107   // returns false if RunCommands() is currently in progress.
108   bool IsReady() const;
109 
110   // Cancels a running sequence. RunCommands() must have been called before a
111   // sequence can be cancelled. Once a sequence is cancelled, the state of the
112   // SequentialCommandRunner will be reset (i.e. IsReady() will return true).
113   // The result of any running HCI command will not be reported to the
114   // corresponding command callback and the result callback will be called
115   // with HostError::kCanceled.
116   //
117   // Depending on the sequence of HCI commands that were previously processed,
118   // the controller will be in an undefined state. The caller is responsible for
119   // sending successive HCI commands to bring the controller back to an expected
120   // state.
121   //
122   // After a call to Cancel(), this object can be immediately reused to queue up
123   // a new HCI command sequence.
124   void Cancel();
125 
126   // Returns true if this currently has any pending commands.
127   bool HasQueuedCommands() const;
128 
129  private:
130   struct QueuedCommand {
131     CommandPacketVariant packet;
132     hci_spec::EventCode complete_event_code;
133     bool is_le_async_command;
134     CommandCompleteCallbackVariant callback;
135     bool wait;
136     std::unordered_set<hci_spec::OpCode> exclusions;
137   };
138 
139   // Try to run the next queued command.
140   // |status| is the result of the most recently completed command.
141   // Aborts the sequence with |status| if it did not succeed.
142   // Completes the sequence with |status| no commands are running or queued.
143   // Runs the next queued command if it doesn't wait for the previous commands.
144   // Runs the next queued command if no commands are running.
145   void TryRunNextQueuedCommand(Result<> status = fit::ok());
146 
147   // Returns true on success, false on failure.
148   bool SendQueuedCommand(QueuedCommand command,
149                          CommandChannel::CommandCallback callback);
150 
151   void Reset();
152   void NotifyStatusAndReset(Result<> status);
153 
154   hci::CommandChannel::WeakPtr cmd_;
155 
156   std::queue<QueuedCommand> command_queue_;
157 
158   // Callback assigned in RunCommands(). If this is non-null then this object is
159   // currently executing a sequence.
160   ResultFunction<> status_callback_;
161 
162   // Number assigned to the current sequence. Each "sequence" begins on a call
163   // to RunCommands() and ends either on a call to Cancel() or when
164   // |status_callback_| has been invoked.
165   //
166   // This number is used to detect cancelation of a sequence from a
167   // CommandCompleteCallback.
168   uint64_t sequence_number_;
169 
170   // Number of commands sent to the controller we are waiting to finish.
171   size_t running_commands_;
172 
173   WeakSelf<SequentialCommandRunner> weak_ptr_factory_;
174 
175   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SequentialCommandRunner);
176 };
177 
178 }  // namespace bt::hci
179