blob: f9b316649cdeda3221f433c4298e26a504bafade [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.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TRANSPORT_COMMAND_CHANNEL_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TRANSPORT_COMMAND_CHANNEL_H_
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/wait.h>
#include <lib/async/dispatcher.h>
#include <lib/fit/function.h>
#include <lib/fit/thread_checker.h>
#include <lib/fpromise/result.h>
#include <lib/sys/inspect/cpp/component.h>
#include <lib/trace/event.h>
#include <lib/zx/channel.h>
#include <zircon/compiler.h>
#include <list>
#include <memory>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <fbl/macros.h>
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/identifier.h"
#include "src/connectivity/bluetooth/core/bt-host/common/inspectable.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/constants.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/control_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/hci_wrapper.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt::hci {
class Transport;
// Represents the HCI Bluetooth command channel. Manages HCI command and event packet control flow.
class CommandChannel final {
public:
// Starts listening for HCI commands and starts handling commands and events.
explicit CommandChannel(HciWrapper* hci);
~CommandChannel();
// Used to identify an individual HCI command<->event transaction.
using TransactionId = size_t;
enum class EventType {
kHciEvent,
kLEMetaEvent,
kVendorEvent,
};
// Queues the given |command_packet| to be sent to the controller and returns a transaction ID.
//
// This call takes ownership of the contents of |command_packet|. |command_packet| MUST represent
// a valid HCI command packet.
//
// |callback| will be called with all events related to the transaction, unless the transaction is
// removed with RemoveQueuedCommand. If the command results in a CommandStatus event, it will be
// sent to this callback before the event with |complete_event_code| is sent.
//
// Synchronous transactions complete with a CommandComplete HCI event. This function is the only
// way to receive a CommandComplete event.
//
// Most asynchronous transactions return the CommandStatus event and another event to indicate
// completion, which should be indicated in |complete_event_code|.
//
// If |complete_event_code| is set to kCommandStatus, the transaction is considered complete when
// the CommandStatus event is received.
//
// |complete_event_code| cannot be a code that has been registered for events via AddEventHandler
// or its related methods.
//
// Returns a ID unique to the command transaction, or zero if the parameters are invalid. This ID
// will be supplied to |callback| in its |id| parameter to identify the transaction.
//
// NOTE: Commands queued are not guaranteed to be finished or sent in order, although commands
// with the same opcode will be sent in order, and commands with the same |complete_event_code|
// and |subevent_code| will be sent in order. If strict ordering of commands is required, use
// SequentialCommandRunner or callbacks for sequencing.
//
// See Bluetooth Core Spec v5.0, Volume 2, Part E, Section 4.4 "Command Flow Control" for more
// information about the HCI command flow control.
using CommandCallback = fit::function<void(TransactionId id, const EventPacket& event)>;
TransactionId SendCommand(
std::unique_ptr<CommandPacket> command_packet, CommandCallback callback,
hci_spec::EventCode complete_event_code = hci_spec::kCommandCompleteEventCode);
// As SendCommand, but the transaction completes on the LE Meta Event. |le_meta_subevent_code| is
// a LE Meta Event subevent code as described in Core Spec v5.2, Vol 4, Part E, Sec 7.7.65.
//
// |le_meta_subevent_code| cannot be a code that has been registered for events via
// AddLEMetaEventHandler.
TransactionId SendLeAsyncCommand(std::unique_ptr<CommandPacket> command_packet,
CommandCallback callback,
hci_spec::EventCode le_meta_subevent_code);
// As SendCommand, but will wait to run this command until there are no commands with with opcodes
// specified in |exclude| from executing. This is useful to prevent running different commands
// that cannot run concurrently (i.e. Inquiry and Connect). Two commands with the same opcode will
// never run simultaneously.
TransactionId SendExclusiveCommand(
std::unique_ptr<CommandPacket> command_packet, CommandCallback callback,
hci_spec::EventCode complete_event_code = hci_spec::kCommandCompleteEventCode,
std::unordered_set<hci_spec::OpCode> exclusions = {});
// As SendExclusiveCommand, but the transaction completes on the LE Meta Event with subevent code
// |le_meta_subevent_code|.
TransactionId SendLeAsyncExclusiveCommand(
std::unique_ptr<CommandPacket> command_packet, CommandCallback callback,
std::optional<hci_spec::EventCode> le_meta_subevent_code,
std::unordered_set<hci_spec::OpCode> exclusions = {});
// If the command identified by |id| has not been sent to the controller, remove it from the send
// queue and return true. In this case, its CommandCallback will not be notified. If the command
// identified by |id| has already been sent to the controller or if it does not exist, this has no
// effect and returns false.
[[nodiscard]] bool RemoveQueuedCommand(TransactionId id);
// Used to identify an individual HCI event handler that was registered with this CommandChannel.
using EventHandlerId = size_t;
// Return values for EventCallbacks.
enum class EventCallbackResult {
// Continue handling this event.
kContinue,
// Remove this event handler.
// NOTE: The event callback may still be called again after it has been removed if the handler
// has already been posted to the dispatcher for subsequent events.
kRemove
};
// Callback invoked to report generic HCI events excluding CommandComplete and CommandStatus
// events.
using EventCallback = fit::function<EventCallbackResult(const EventPacket& event_packet)>;
// Registers an event handler for HCI events that match |event_code|. Incoming HCI event packets
// that are not associated with a pending command sequence will be posted on the given
// |dispatcher| via the given |event_callback|. The returned ID can be used to unregister a
// previously registered event handler.
//
// |event_callback| will be invoked for all HCI event packets that match |event_code|, except for:
//
// - HCI_CommandStatus events
// - HCI_CommandComplete events
// - The completion event of the currently pending command packet, if any
//
// Returns an ID if the handler was successfully registered. Returns zero in case of an error.
//
// Multiple handlers can be registered for a given |event_code| at a time. All handlers that are
// registered will be called with a reference to the event.
//
// If an asynchronous command is queued which completes on |event_code|, this method returns zero.
// It is good practice to avoid using asynchronous commands and event handlers for the same event
// code. SendCommand allows for queueing multiple asynchronous commands with the same callback.
// Alternately a long-lived event handler can be registered with Commands completing on
// CommandStatus.
//
// The following values for |event_code| cannot be passed to this method:
//
// - HCI_Command_Complete event code
// - HCI_Command_Status event code
// - HCI_LE_Meta event code (use AddLEMetaEventHandler instead)
// - HCI_Vendor_Debug event code (use AddVendorEventHandler instead)
EventHandlerId AddEventHandler(hci_spec::EventCode event_code, EventCallback event_callback);
// Works just like AddEventHandler but the passed in event code is only valid within the LE Meta
// Event sub-event code namespace. |event_callback| will get invoked whenever the controller sends
// a LE Meta Event with a matching subevent code.
EventHandlerId AddLEMetaEventHandler(hci_spec::EventCode le_meta_subevent_code,
EventCallback event_callback);
// Works just like AddEventHandler but the passed in event code is only valid for vendor related
// debugging events. The event_callback will get invoked whenever the controller sends one of
// these vendor debugging events with a matching subevent code.
EventHandlerId AddVendorEventHandler(hci_spec::EventCode vendor_subevent_code,
EventCallback event_callback);
// Removes a previously registered event handler. Does nothing if an event handler with the given
// |id| could not be found.
void RemoveEventHandler(EventHandlerId id);
// Set callback that will be called when a command times out after kCommandTimeout. This is
// distinct from channel closure.
void set_channel_timeout_cb(fit::closure timeout_cb) {
channel_timeout_cb_ = std::move(timeout_cb);
}
// Attach command_channel inspect node as a child node of |parent|.
static constexpr const char* kInspectNodeName = "command_channel";
void AttachInspect(inspect::Node& parent, const std::string& name = kInspectNodeName);
fxl::WeakPtr<CommandChannel> AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
private:
TransactionId SendExclusiveCommandInternal(
std::unique_ptr<CommandPacket> command_packet, CommandCallback callback,
hci_spec::EventCode complete_event_code,
std::optional<hci_spec::EventCode> le_meta_subevent_code = std::nullopt,
std::unordered_set<hci_spec::OpCode> exclusions = {});
// Data related to a queued or running command.
//
// When a command is sent to the controller, it is placed in the pending_transactions_ map. It
// remains in the pending_transactions_ map until it is completed - asynchronous commands remain
// until their complete_event_code_ is received.
//
// There are a number of reasons a command may be queued before sending:
//
// - An asynchronous command is waiting on the same event code
// - A command with an opcode that is in the exclusions set for this transaction is pending
// - We cannot send any commands because of the limit of outstanding command packets from the
// controller Queued commands are held in the send_queue_ and are sent when possible, FIFO (but
// skipping commands that cannot be sent)
class TransactionData {
public:
TransactionData(TransactionId id, hci_spec::OpCode opcode,
hci_spec::EventCode complete_event_code,
std::optional<hci_spec::EventCode> le_meta_subevent_code,
std::unordered_set<hci_spec::OpCode> exclusions, CommandCallback callback);
~TransactionData();
// Starts the transaction timer, which will call timeout_cb if it's not completed in time.
void Start(fit::closure timeout_cb, zx::duration timeout);
// Completes the transaction with |event|. For asynchronous commands, this should be called with
// the status event (the complete event is handled separately by the event handler).
void Complete(std::unique_ptr<EventPacket> event);
// Cancels the transaction timeout and erases the callback so it isn't called upon destruction.
void Cancel();
// Makes an EventCallback that calls |callback_| correctly.
EventCallback MakeCallback();
hci_spec::EventCode complete_event_code() const { return complete_event_code_; }
std::optional<hci_spec::EventCode> le_meta_subevent_code() const {
return le_meta_subevent_code_;
}
hci_spec::OpCode opcode() const { return opcode_; }
TransactionId id() const { return transaction_id_; }
// The set of opcodes in progress that will hold this transaction in queue.
const std::unordered_set<hci_spec::OpCode>& exclusions() const { return exclusions_; }
EventHandlerId handler_id() const { return handler_id_; }
void set_handler_id(EventHandlerId id) { handler_id_ = id; }
private:
TransactionId transaction_id_;
hci_spec::OpCode opcode_;
hci_spec::EventCode complete_event_code_;
std::optional<hci_spec::EventCode> le_meta_subevent_code_;
std::unordered_set<hci_spec::OpCode> exclusions_;
CommandCallback callback_;
async::TaskClosure timeout_task_;
// If non-zero, the id of the handler registered for this transaction.
// Always zero if this transaction is synchronous.
EventHandlerId handler_id_;
DISALLOW_COPY_ASSIGN_AND_MOVE(TransactionData);
};
// Adds an internal event handler for |data| if one does not exist yet and another transaction is
// not waiting on the same event. Used to add expiring event handlers for asynchronous commands.
void MaybeAddTransactionHandler(TransactionData* data);
// Represents a queued command packet.
struct QueuedCommand {
QueuedCommand(std::unique_ptr<CommandPacket> command_packet,
std::unique_ptr<TransactionData> data);
QueuedCommand() = default;
QueuedCommand(QueuedCommand&& other) = default;
QueuedCommand& operator=(QueuedCommand&& other) = default;
std::unique_ptr<CommandPacket> packet;
std::unique_ptr<TransactionData> data;
};
// Data stored for each event handler registered.
struct EventHandlerData {
EventHandlerId handler_id;
hci_spec::EventCode event_code;
// Defines how event_code should be interpreted. For example, if the event_type is kLEMetaEvent,
// the event_code is an LE Meta Subevent code.
EventType event_type;
// For asynchronous transaction event handlers, the pending command opcode.
// kNoOp if this is a static event handler.
hci_spec::OpCode pending_opcode;
EventCallback event_callback;
// Returns true if handler is for async command transaction, or false if handler is a static
// event handler.
bool is_async() const { return pending_opcode != hci_spec::kNoOp; }
};
// Finds the event handler for |code|. Returns nullptr if one doesn't exist.
EventHandlerData* FindEventHandler(hci_spec::EventCode code);
// Finds the LE Meta Event handler for |le_meta_subevent_code|. Returns nullptr if one doesn't
// exist.
EventHandlerData* FindLEMetaEventHandler(hci_spec::EventCode le_meta_subevent_code);
// Finds the Vendor Event handler for |vendor_subevent_code|. Returns nullptr if one doesn't
// exist.
EventHandlerData* FindVendorEventHandler(hci_spec::EventCode vendor_subevent_code);
// Removes internal event handler structures for |id|.
void RemoveEventHandlerInternal(EventHandlerId id);
// Sends any queued commands that can be processed unambiguously and complete.
void TrySendQueuedCommands();
// Sends |command|, adding an internal event handler if asynchronous.
void SendQueuedCommand(QueuedCommand&& cmd);
// Creates a new event handler entry in the event handler map and returns its ID. The event_code
// should correspond to the event_type provided. For example, if event_type is kLEMetaEvent, then
// event_code will be interpreted as a LE Meta Subevent code.
EventHandlerId NewEventHandler(hci_spec::EventCode event_code, EventType event_type,
hci_spec::OpCode pending_opcode, EventCallback event_callback);
// Notifies any matching event handler for |event|.
void NotifyEventHandler(std::unique_ptr<EventPacket> event);
// Notifies handlers for Command Status and Command Complete Events. This function marks opcodes
// that have transactions pending as complete by removing them and calling their callbacks.
void UpdateTransaction(std::unique_ptr<EventPacket> event);
// Event handler.
void OnEvent(std::unique_ptr<EventPacket> event);
// Opcodes of commands that we have sent to the controller but not received a status update from.
// New commands with these opcodes can't be sent because they are indistinguishable from ones we
// need to get the result of.
std::unordered_map<hci_spec::OpCode, std::unique_ptr<TransactionData>> pending_transactions_;
// TransactionId counter.
UintInspectable<TransactionId> next_transaction_id_;
// EventHandlerId counter.
UintInspectable<size_t> next_event_handler_id_;
// Used to assert that certain public functions are only called on the creation thread.
fit::thread_checker thread_checker_;
// The HCI we use to send/receive HCI commands/events.
HciWrapper* hci_;
// Callback called when a command times out.
fit::closure channel_timeout_cb_;
// The HCI command queue. This queue is not necessarily sent in order, but commands with the same
// opcode or that wait on the same completion event code are sent first-in, first-out.
std::list<QueuedCommand> send_queue_;
// Contains the current count of commands we ae allowed to send to the controller. This is
// decremented when we send a command and updated when we receive a CommandStatus or
// CommandComplete event with the Num HCI Command Packets parameter.
//
// Accessed only from the I/O thread and thus not guarded.
UintInspectable<size_t> allowed_command_packets_;
// Mapping from event handler IDs to handler data.
std::unordered_map<EventHandlerId, EventHandlerData> event_handler_id_map_;
// Mapping from event code to the event handlers that were registered to handle that event code.
std::unordered_multimap<hci_spec::EventCode, EventHandlerId> event_code_handlers_;
// Mapping from LE Meta Event Subevent code to the event handlers that were registered to handle
// that event code.
std::unordered_multimap<hci_spec::EventCode, EventHandlerId> le_meta_subevent_code_handlers_;
// Mapping from Vendor Subevent code to the event handlers that were registered to handle that
// event code.
std::unordered_multimap<hci_spec::EventCode, EventHandlerId> vendor_subevent_code_handlers_;
// Command channel inspect node.
inspect::Node command_channel_node_;
fxl::WeakPtrFactory<CommandChannel> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(CommandChannel);
};
} // namespace bt::hci
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TRANSPORT_COMMAND_CHANNEL_H_