blob: a055d9e77cf4cf22eebf0458a5acdcc1f9998bad [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <queue>
#include <lib/async/dispatcher.h>
#include "garnet/drivers/bluetooth/lib/hci/command_channel.h"
#include "garnet/drivers/bluetooth/lib/hci/control_packets.h"
#include "lib/fxl/functional/cancelable_callback.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_ptr.h"
#include "lib/fxl/synchronization/thread_checker.h"
namespace btlib {
namespace hci {
class Transport;
// A SequentialCommandRunner can be used to chain HCI commands one after another
// such that each command in the sequence is sent to the controller only after
// the previous command has completed successfully. When a command fails due to
// an error status (in a HCI_Command_Status or HCI_Command_Complete event) or a
// timeout, the entire sequence is interrupted and an error callback is reported
// back to the caller.
//
// Only commands that terminate with the HCI_Command_Complete event are
// currently supported.
//
// This class is not thread-safe. The dispatcher that is provided during
// initialization must be bound to the thread on which the instance of
// SequentialCommandRunner is being constructed.
class SequentialCommandRunner final {
public:
SequentialCommandRunner(async_t* dispatcher,
fxl::RefPtr<Transport> transport);
~SequentialCommandRunner();
// Adds a HCI command packet and an optional callback to invoke with the
// completion event to the command sequence. Cannot be called once
// RunCommands() has been called.
using CommandCompleteCallback =
std::function<void(const EventPacket& command_complete)>;
void QueueCommand(
std::unique_ptr<CommandPacket> command_packet,
const CommandCompleteCallback& callback = CommandCompleteCallback());
// Runs all the queued commands. Once this is called no new commands can be
// queued. This method will return before queued commands have been run.
// |status_callback| is called with the status of the last command run,
// or kSuccess if all commands returned HCI_Command_Complete.
//
// Once RunCommands() has been called this instance will not be ready for
// re-use until |status_callback| gets run. At that point new commands can be
// queued and run (see IsReady()).
//
// RunCommands() will always send the first queued HCI command to
// CommandChannel even if it is followed by a call to Cancel().
using StatusCallback = fit::function<void(Status status)>;
void RunCommands(StatusCallback status_callback);
// Returns true if commands can be queued and run on this instance. This
// returns false if RunCommands() is currently in progress.
bool IsReady() const;
// Cancels a running sequence. RunCommands() must have been called before a
// sequence can be cancelled. Once a sequence is cancelled, the state of the
// SequentialCommandRunner will be reset (i.e. IsReady() will return true).
// The result of any pending HCI command will be ignored and callbacks will
// not be invoked.
//
// Depending on the sequence of HCI commands that were previously processed,
// the controller will be in an undefined state. The caller is responsible for
// sending successive HCI commands to bring the controller back to an expected
// state.
//
// After a call to Cancel(), this object can be immediately reused to queue up
// a new HCI command sequence.
void Cancel();
// Returns true if this currently has any pending commands.
bool HasQueuedCommands() const;
private:
void RunNextQueuedCommand();
void Reset();
void NotifyStatusAndReset(Status status);
async_t* dispatcher_;
fxl::RefPtr<Transport> transport_;
using CommandQueue = std::queue<
std::pair<std::unique_ptr<CommandPacket>, CommandCompleteCallback>>;
CommandQueue command_queue_;
// Callback assigned in RunCommands(). If this is non-null then this object is
// currently executing a sequence.
StatusCallback status_callback_;
// Number assigned to the current sequence. Each "sequence" begins on a call
// to RunCommands() and ends either on a call to Cancel() or when
// |status_callback_| has been invoked.
//
// This number is used to detect cancelation of a sequence from a
// CommandCompleteCallback.
uint64_t sequence_number_;
fxl::ThreadChecker thread_checker_;
// The callback we pass to the HCI CommandChannel for command execution.
// This can be cancelled and erased at any time.
fxl::CancelableCallback<void(CommandChannel::TransactionId,
const EventPacket&)>
command_callback_;
FXL_DISALLOW_COPY_AND_ASSIGN(SequentialCommandRunner);
};
} // namespace hci
} // namespace btlib