blob: 6dd9de486bc208c56c37d5337cdeaabf49170c58 [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_HCI_ACL_DATA_CHANNEL_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_HCI_ACL_DATA_CHANNEL_H_
#include <lib/async/cpp/wait.h>
#include <lib/async/dispatcher.h>
#include <lib/fit/function.h>
#include <lib/zx/channel.h>
#include <zircon/compiler.h>
#include <list>
#include <mutex>
#include <queue>
#include <unordered_map>
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_packet.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/command_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/control_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/hci_constants.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "src/lib/fxl/synchronization/thread_checker.h"
namespace bt {
namespace hci {
class Transport;
// Represents the controller data buffer settings for the BR/EDR or LE
// transports.
class DataBufferInfo {
public:
// Initialize fields to non-zero values.
DataBufferInfo(size_t max_data_length, size_t max_num_packets);
// The default constructor sets all fields to zero. This can be used to
// represent a data buffer that does not exist (e.g. the controller has a
// single shared buffer and no dedicated LE buffer.
DataBufferInfo();
// The maximum length (in octets) of the data portion of each HCI ACL data
// packet that the controller is able to accept.
size_t max_data_length() const { return max_data_length_; }
// Returns the total number of HCI ACL data packets that can be stored in the
// data buffer represented by this object.
size_t max_num_packets() const { return max_num_packets_; }
// Returns true if both fields are set to zero.
bool IsAvailable() const { return max_data_length_ && max_num_packets_; }
// Comparison operators.
bool operator==(const DataBufferInfo& other) const;
bool operator!=(const DataBufferInfo& other) const { return !(*this == other); }
private:
size_t max_data_length_;
size_t max_num_packets_;
};
// Represents the Bluetooth ACL Data channel and manages the Host<->Controller
// ACL data flow control.
//
// This currently only supports the Packet-based Data Flow Control as defined in
// Core Spec v5.0, Vol 2, Part E, Section 4.1.1.
using ACLPacketPredicate =
fit::function<bool(const ACLDataPacketPtr& packet, l2cap::ChannelId channel_id)>;
class ACLDataChannel final {
public:
enum class PacketPriority { kHigh, kLow };
ACLDataChannel(Transport* transport, zx::channel hci_acl_channel);
~ACLDataChannel();
// Starts listening on the HCI ACL data channel and starts handling data flow
// control. |bredr_buffer_info| represents the controller's data buffering
// capacity for the BR/EDR transport and the |le_buffer_info| represents Low
// Energy buffers. At least one of these (BR/EDR vs LE) must contain non-zero
// values per Core Spec v5.0 Vol 2, Part E, Sec 4.1.1:
//
// - A LE only controller will have LE buffers only.
// - A BR/EDR-only controller will have BR/EDR buffers only.
// - A dual-mode controller will have BR/EDR buffers and MAY have LE buffers
// if the BR/EDR buffer is not shared between the transports.
//
// As this class is intended to support flow-control for both, this function
// should be called based on what is reported by the controller.
void Initialize(const DataBufferInfo& bredr_buffer_info, const DataBufferInfo& le_buffer_info);
// 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();
// Assigns a handler callback for received ACL data packets. |rx_callback| will be posted on
// |dispatcher| and shall take ownership of each packet received from the controller.
void SetDataRxHandler(ACLPacketHandler rx_callback, async_dispatcher_t* rx_dispatcher);
// Queues the given ACL data packet to be sent to the controller. Returns
// false if the packet cannot be queued up, e.g. if the size of |data_packet|
// exceeds the MTU for the link type set in RegisterLink().
//
// |data_packet| is passed by value, meaning that ACLDataChannel will take
// ownership of it. |data_packet| must represent a valid ACL data packet.
//
// |channel_id| must match the l2cap channel that the packet is being sent to. It is needed to
// determine what channel l2cap packet fragments are being sent to when revoking queued packets
// for specific channels that have closed. If the packet does not contain a fragment of an l2cap
// packet, |channel_id| should be set to |l2cap::kInvalidChannelId|.
//
// |priority| indicates the order this packet should be dispatched off of the queue relative to
// packets of other priorities. Note that high priority packets may still wait behind low priority
// packets that have already been sent to the controller.
bool SendPacket(ACLDataPacketPtr data_packet, l2cap::ChannelId channel_id,
PacketPriority priority = PacketPriority::kLow);
// Queues the given list of ACL data packets to be sent to the controller. The
// behavior is identical to that of SendPacket() with the guarantee that all
// packets that are in |packets| are queued atomically. If any packet's handle is not registered
// in the allowlist, then none will be queued.
//
// Takes ownership of the contents of |packets|. Returns false if |packets|
// contains an element that exceeds the MTU for its link type or |packets| is empty.
//
// |channel_id| must match the l2cap channel that all packets is being sent to. It is needed to
// determine what channel l2cap packet fragments are being sent to when revoking queued packets
// for channels that have closed. If the packets do not contain a fragment of an l2cap
// packet, |channel_id| should be set to |l2cap::kInvalidChannelId|.
//
// |priority| indicates the order this packet should be dispatched off of the queue relative to
// packets of other priorities. Note that high priority packets may still wait behind low priority
// packets that have already been sent to the controller.
bool SendPackets(LinkedList<ACLDataPacket> packets, l2cap::ChannelId channel_id,
PacketPriority priority = PacketPriority::kLow);
// Allowlist packets destined for the link identified by |handle| (of link type |ll_type|) for
// submission to the controller.
//
// Failure to register a link before sending packets will result in the packets
// being dropped immediately. A handle must not be registered again until after UnregisterLink has
// been called on that handle.
void RegisterLink(hci::ConnectionHandle handle, Connection::LinkType ll_type);
// Cleans up all outgoing data buffering state related to the logical link
// with the given |handle|. This must be called upon disconnection of a link
// to ensure that stale outbound packets are filtered out of the send queue.
// All future packets sent to this link will be dropped.
//
// |RegisterLink| must be called before |UnregisterLink| for the same handle.
//
// |UnregisterLink| does not clear the controller packet count, so |ClearControllerPacketCount|
// must be called after |UnregisterLink| and the HCI Disconnection Complete event has been
// received.
void UnregisterLink(hci::ConnectionHandle handle);
// Removes all queued data packets for which |predicate| returns true.
void DropQueuedPackets(ACLPacketPredicate predicate);
// Resets controller packet count for |handle| so that controller buffer credits can be reused.
// This must be called on the HCI_Disconnection_Complete event to notify ACLDataChannel that
// packets in the controller's buffer for |handle| have been flushed. See Core Spec v5.1, Vol 2,
// Part E, Section 4.3. This must be called after |UnregisterLink|.
void ClearControllerPacketCount(hci::ConnectionHandle handle);
// Returns the underlying channel handle.
const zx::channel& channel() const { return channel_; }
// Returns the BR/EDR buffer information that the channel was initialized
// with.
const DataBufferInfo& GetBufferInfo() const;
// Returns the LE buffer information that the channel was initialized with.
// This defaults to the BR/EDR buffers if the controller does not have a
// dedicated LE buffer.
const DataBufferInfo& GetLEBufferInfo() const;
private:
// Represents a queued ACL data packet.
struct QueuedDataPacket {
QueuedDataPacket(Connection::LinkType ll_type, l2cap::ChannelId channel_id,
PacketPriority priority, ACLDataPacketPtr packet)
: ll_type(ll_type), channel_id(channel_id), priority(priority), packet(std::move(packet)) {}
QueuedDataPacket() = default;
QueuedDataPacket(QueuedDataPacket&& other) = default;
QueuedDataPacket& operator=(QueuedDataPacket&& other) = default;
Connection::LinkType ll_type;
l2cap::ChannelId channel_id;
PacketPriority priority;
ACLDataPacketPtr packet;
};
// Drops all packets that |predicate| returns true for. This locked version is required for
// UnregisterLink to call this method.
void DropQueuedPacketsLocked(ACLPacketPredicate predicate) __TA_REQUIRES(send_mutex_);
// Returns the data buffer MTU for the given connection.
size_t GetBufferMTU(Connection::LinkType ll_type) const;
// Handler for the HCI Number of Completed Packets Event, used for
// packet-based data flow control.
CommandChannel::EventCallbackResult NumberOfCompletedPacketsCallback(const EventPacket& event);
// Tries to send the next batch of queued data packets if the controller has
// any space available. All packets in higher priority queues will be sent before packets in lower
// priority queues.
void TrySendNextQueuedPacketsLocked() __TA_REQUIRES(send_mutex_);
// Returns the number of BR/EDR packets for which the controller has available
// space to buffer.
size_t GetNumFreeBREDRPacketsLocked() const __TA_REQUIRES(send_mutex_);
// Returns the number of LE packets for which controller has available space
// to buffer. Must be called from a locked context.
size_t GetNumFreeLEPacketsLocked() const __TA_REQUIRES(send_mutex_);
// Decreases the total number of sent packets count by the given amount. Must
// be called from a locked context.
void DecrementTotalNumPacketsLocked(size_t count) __TA_REQUIRES(send_mutex_);
// Decreases the total number of sent packets count for LE by the given
// amount. Must be called from a locked context.
void DecrementLETotalNumPacketsLocked(size_t count) __TA_REQUIRES(send_mutex_);
// Increments the total number of sent packets count by the given amount. Must
// be called from a locked context.
void IncrementTotalNumPacketsLocked(size_t count) __TA_REQUIRES(send_mutex_);
// Increments the total number of sent LE packets count by the given amount.
// Must be called from a locked context.
void IncrementLETotalNumPacketsLocked(size_t count) __TA_REQUIRES(send_mutex_);
// Read Ready Handler for |channel_|
void OnChannelReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal);
// Handler for HCI_Buffer_Overflow_event
CommandChannel::EventCallbackResult DataBufferOverflowCallback(const EventPacket& event);
// Used to assert that certain public functions are only called on the
// creation thread.
fxl::ThreadChecker thread_checker_;
// The Transport object that owns this instance.
Transport* transport_; // weak;
// The channel that we use to send/receive HCI ACL data packets.
zx::channel channel_;
// Wait object for |channel_|
async::WaitMethod<ACLDataChannel, &ACLDataChannel::OnChannelReady> channel_wait_{this};
// True if this instance has been initialized through a call to Initialize().
std::atomic_bool is_initialized_;
// The event handler ID for the Number Of Completed Packets event.
CommandChannel::EventHandlerId num_completed_packets_event_handler_id_;
// The event handler ID for the Data Buffer Overflow event.
CommandChannel::EventHandlerId data_buffer_overflow_event_handler_id_;
// The dispatcher used for posting tasks on the HCI transport I/O thread.
async_dispatcher_t* io_dispatcher_;
// The current handler for incoming data and the dispatcher on which to run
// it.
std::mutex rx_mutex_;
ACLPacketHandler rx_callback_ __TA_GUARDED(rx_mutex_);
async_dispatcher_t* rx_dispatcher_ __TA_GUARDED(rx_mutex_);
// BR/EDR data buffer information. This buffer will not be available on
// LE-only controllers.
DataBufferInfo bredr_buffer_info_;
// LE data buffer information. This buffer will not be available on
// BR/EDR-only controllers (which we do not support) and MAY be available on
// dual-mode controllers. We maintain that if this buffer is not available,
// then the BR/EDR buffer MUST be available.
DataBufferInfo le_buffer_info_;
// Mutex that guards access to data transmission related members below.
std::mutex send_mutex_;
// The current count of the number of ACL data packets that have been sent to
// the controller. |le_num_sent_packets_| is ignored if the controller uses
// one buffer for LE and BR/EDR.
size_t num_sent_packets_ __TA_GUARDED(send_mutex_);
size_t le_num_sent_packets_ __TA_GUARDED(send_mutex_);
// The ACL data packet queue contains the data packets that are waiting to be
// sent to the controller.
// TODO(armansito): Use priority_queue based on L2CAP channel priority.
// TODO(NET-1211): Keep a separate queue for each open connection. Benefits:
// * Helps address the packet-prioritization TODO above.
// * Also: having separate queues, which know their own
// Connection::LinkType, would let us replace std::list<QueuedDataPacket>
// with LinkedList<ACLDataPacket> which has a more efficient
// memory layout.
using DataPacketQueue = std::list<QueuedDataPacket>;
DataPacketQueue send_queue_ __TA_GUARDED(send_mutex_);
// Returns an iterator to the location new packets should be inserted into |send_queue_| based on
// their |priority|:
// If |priority| is |kLow|: returns past-the-end of |send_queue_|.
// If |priority| is |kHigh|: returns the location of the first |kLow| priority packet.
DataPacketQueue::iterator SendQueueInsertLocationForPriority(PacketPriority priority)
__TA_REQUIRES(send_mutex_);
// Stores the link type of connections on which we have a pending packet that
// has been sent to the controller. Entries are removed on the HCI Number Of
// Completed Packets event.
struct PendingPacketData {
PendingPacketData() = default;
// We initialize the packet count at 1 since a new entry will only be
// created once.
PendingPacketData(Connection::LinkType ll_type) : ll_type(ll_type), count(1u) {}
Connection::LinkType ll_type;
size_t count;
};
std::unordered_map<ConnectionHandle, PendingPacketData> pending_links_ __TA_GUARDED(send_mutex_);
// Stores links registered by RegisterLink
std::unordered_map<hci::ConnectionHandle, Connection::LinkType> registered_links_
__TA_GUARDED(send_mutex_);
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ACLDataChannel);
};
} // namespace hci
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_HCI_ACL_DATA_CHANNEL_H_