blob: 6c465b875f2f6d6dd993a0a7cdedb829c7e0e334 [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 <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <unordered_map>
#include <async/wait.h>
#include <zircon/compiler.h>
#include <zx/channel.h>
#include "garnet/drivers/bluetooth/lib/common/byte_buffer.h"
#include "garnet/drivers/bluetooth/lib/common/optional.h"
#include "garnet/drivers/bluetooth/lib/hci/control_packets.h"
#include "garnet/drivers/bluetooth/lib/hci/hci.h"
#include "garnet/drivers/bluetooth/lib/hci/hci_constants.h"
#include "lib/fsl/tasks/message_loop.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"
#include "lib/fxl/tasks/task_runner.h"
namespace bluetooth {
namespace hci {
class Transport;
// Represents the HCI Bluetooth command channel. Manages HCI command and event
// packet control flow.
//
// TODO(armansito): I don't imagine many cases in which we will want to queue up
// HCI commands from the data thread. Consider making this class fully single
// threaded and removing the locks.
class CommandChannel final {
public:
// |hci_command_channel| is a Zircon channel construct that can receive
// Bluetooth HCI command and event packets, in which the remote end is
// implemented by the underlying Bluetooth HCI device driver.
//
// |transport| is the Transport instance that owns this CommandChannel.
CommandChannel(Transport* transport, zx::channel hci_command_channel);
~CommandChannel();
// Starts listening on the HCI command channel and starts handling commands
// and events.
void Initialize();
// Unregisters event handlers and cleans up.
// NOTE: Initialize() and ShutDown() MUST be called on the same thread. These
// methods are not thread-safe.
void ShutDown();
// Used to identify an individual HCI command<->event transaction.
using TransactionId = size_t;
// Callback invoked to report the completion of a HCI command.
using CommandCompleteCallback =
std::function<void(TransactionId id, const EventPacket& event_packet)>;
// Callback invoked to report the status of a pending HCI command. This can be
// following the receipt of a HCI_Command_Status event from the controller OR
// due to a command timeout.
using CommandStatusCallback =
std::function<void(TransactionId id, Status status)>;
// Queues the given |command_packet| to be sent to the controller and returns
// a transaction ID. The given callbacks will be posted on |task_runner| to be
// processed on the appropriate thread requested by the caller.
//
// This call will take ownership of the contents of |command_packet|.
// |command_packet| MUST represent a valid HCI command packet.
//
// |status_callback| will be called if the controller responds to the command
// with a CommandStatus HCI event.
//
// |complete_callback| will be called if the controller responds to the
// command with an event with the given |complete_event_code|. Most HCI
// commands are marked as complete using the CommandComplete HCI event,
// however some command sequences use different events, as specified in the
// Bluetooth Core Specification.
//
// When a caller provides a value for |complete_event_code| the caller can
// also optionally provide a filter |complete_event_matcher| which will be
// used to determine whether the event should match this command sequence. If
// a filter rejects the event (by returning false), the event will be passed
// to the handler registered via AddEventHander(). This callback will be run
// on the I/O thread and must complete synchronously.
//
// Returns a transaction ID that is unique to the initiated command sequence.
// This can be used to identify the command sequence by comparing it to the
// |id| parameter in a CommandCompleteCallback.
//
// If the controller does not respond to with the expected
// |complete_event_code| within a certain amount of time (see
// kCommandTimeoutMs in hci_constants.h) the command will time out. This will
// be signalled to the caller by invoking |status_callback| with the |status|
// parameter set to the special status code Status::kCommandTimeout.
//
// 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 EventMatcher = std::function<bool(const EventPacket& event)>;
TransactionId SendCommand(
std::unique_ptr<CommandPacket> command_packet,
fxl::RefPtr<fxl::TaskRunner> task_runner,
const CommandCompleteCallback& complete_callback,
const CommandStatusCallback& status_callback = {},
const EventCode complete_event_code = kCommandCompleteEventCode,
const EventMatcher& complete_event_matcher = {});
// Used to identify an individual HCI event handler that was registered with
// this CommandChannel.
using EventHandlerId = size_t;
// Callback invoked to report generic HCI events excluding CommandComplete and
// CommandStatus events.
using EventCallback = std::function<void(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 |task_runner| 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 a non-zero ID if the handler was successfully registered. Returns
// zero in case of an error.
//
// Only one handler can be registered for a given |event_code| at a time. If a
// handler was previously registered for the given |event_code|, this method
// returns zero.
//
// If |task_runner| corresponds to the I/O thread's task runner, then the
// callback will be executed as soon as the event is received from the command
// channel. No delayed task posting will occur.
//
// 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).
EventHandlerId AddEventHandler(EventCode event_code,
const EventCallback& event_callback,
fxl::RefPtr<fxl::TaskRunner> task_runner);
// 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.
//
// |subevent_code| cannot be 0.
EventHandlerId AddLEMetaEventHandler(
EventCode subevent_code,
const EventCallback& event_callback,
fxl::RefPtr<fxl::TaskRunner> task_runner);
// Removes a previously registered event handler. Does nothing if an event
// handler with the given |id| could not be found.
void RemoveEventHandler(EventHandlerId id);
// Returns the underlying channel handle.
const zx::channel& channel() const { return channel_; }
private:
// Represents a pending HCI command.
struct PendingTransactionData {
TransactionId id;
OpCode opcode;
EventCode complete_event_code;
EventMatcher complete_event_matcher;
CommandStatusCallback status_callback;
CommandCompleteCallback complete_callback;
fxl::RefPtr<fxl::TaskRunner> task_runner;
};
// Represents a queued command packet.
struct QueuedCommand {
QueuedCommand(TransactionId id,
std::unique_ptr<CommandPacket> command_packet,
const CommandStatusCallback& status_callback,
const CommandCompleteCallback& complete_callback,
fxl::RefPtr<fxl::TaskRunner> task_runner,
EventCode complete_event_code,
const EventMatcher& complete_event_matcher);
QueuedCommand() = default;
QueuedCommand(QueuedCommand&& other) = default;
QueuedCommand& operator=(QueuedCommand&& other) = default;
std::unique_ptr<CommandPacket> packet;
PendingTransactionData transaction_data;
};
// Data stored for each event handler registered via AddEventHandler.
struct EventHandlerData {
EventHandlerId id;
EventCode event_code;
EventCallback event_callback;
bool is_le_meta_subevent;
fxl::RefPtr<fxl::TaskRunner> task_runner;
};
// Tries to send the next queued command if there are any queued commands and
// there is no currently pending command.
void TrySendNextQueuedCommand();
// If the given event packet corresponds to the currently pending command,
// this method completes the transaction and sends the next queued command, if
// any. Returns false if the event needs to be handled by an event handler and
// does not take ownership of it.
bool HandlePendingCommandComplete(std::unique_ptr<EventPacket>&& event);
// If the given CommandStatus event packet corresponds to the currently
// pending command and notifes the transaction's |status_callback|.
void HandlePendingCommandStatus(const EventPacket& event);
// Returns a pointer to the currently pending command. Return nullptr if no
// command is currently pending.
PendingTransactionData* GetPendingCommand();
// Sets the currently pending command. If |command| is nullptr, this will
// clear the currently pending command. This also cancels the HCI command
// timeout callback for the current pending command.
void SetPendingCommand(PendingTransactionData* command);
// Creates a new event handler entry in the event handler map and returns its
// ID.
EventHandlerId NewEventHandler(EventCode event_code,
bool is_le_meta,
const EventCallback& event_callback,
fxl::RefPtr<fxl::TaskRunner> task_runner)
__TA_REQUIRES(event_handler_mutex_);
// Notifies a matching event handler for the given event.
void NotifyEventHandler(std::unique_ptr<EventPacket> event);
// Read ready handler for |channel_|
async_wait_result_t OnChannelReady(async_t* async,
zx_status_t status,
const zx_packet_signal_t* signal);
// TransactionId counter.
std::atomic_size_t next_transaction_id_ __TA_GUARDED(send_queue_mutex_);
// EventHandlerId counter.
std::atomic_size_t next_event_handler_id_ __TA_GUARDED(event_handler_mutex_);
// Used to assert that certain public functions are only called on the
// creation thread.
fxl::ThreadChecker thread_checker_;
// The Transport object that owns this CommandChannel.
Transport* transport_; // weak
// The channel we use to send/receive HCI commands/events.
zx::channel channel_;
// Wait object for |channel_|
async::Wait channel_wait_;
// True if this CommandChannel has been initialized through a call to
// Initialize().
std::atomic_bool is_initialized_;
// The task runner used for posting tasks on the HCI transport I/O thread.
fxl::RefPtr<fxl::TaskRunner> io_task_runner_;
// Guards |send_queue_|. |send_queue_| can get accessed by threads that call
// SendCommand() as well as from |io_thread_|.
std::mutex send_queue_mutex_;
// The HCI command queue. These are the commands that have been queued to be
// sent to the controller.
// TODO(armansito): Store std::unique_ptr<QueuedCommand>?
std::queue<QueuedCommand> send_queue_ __TA_GUARDED(send_queue_mutex_);
// Contains the currently pending HCI command packet. While controllers may
// allow more than one packet to be pending at a given point in time, we only
// send one packet at a time to keep things simple.
//
// Accessed only from the I/O thread and thus not guarded.
common::Optional<PendingTransactionData> pending_command_;
// The command timeout callback assigned to the current pending command.
fxl::CancelableClosure pending_cmd_timeout_;
// Guards |event_handler_id_map_| and |event_code_handlers_| which can be
// accessed by both the public EventHandler methods and |io_thread_|.
std::mutex event_handler_mutex_;
// Mapping from event handler IDs to handler data.
std::unordered_map<EventHandlerId, EventHandlerData> event_handler_id_map_
__TA_GUARDED(event_handler_mutex_);
// Mapping from event code to the event handler that was registered to handle
// that event code.
std::unordered_map<EventCode, EventHandlerId> event_code_handlers_
__TA_GUARDED(event_handler_mutex_);
// Mapping from LE Meta Event Subevent code to the event handler that was
// registered to handle that event code.
std::unordered_map<EventCode, EventHandlerId> subevent_code_handlers_
__TA_GUARDED(event_handler_mutex_);
FXL_DISALLOW_COPY_AND_ASSIGN(CommandChannel);
};
} // namespace hci
} // namespace bluetooth