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