blob: 50d604f8c65755d1899850a8aa570f93bcf58c71 [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_L2CAP_CHANNEL_MANAGER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_
#include <lib/async/dispatcher.h>
#include <zircon/compiler.h>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <fbl/macros.h>
#include <trace/event.h>
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_packet.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/hci.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/le_signaling_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h"
#include "src/lib/fxl/synchronization/thread_checker.h"
namespace bt {
namespace hci {
class Transport;
} // namespace hci
namespace l2cap {
namespace internal {
class LogicalLink;
} // namespace internal
// ChannelManager implements the "Channel Manager" control block of L2CAP. In
// particular:
//
// * It acts as a routing table for incoming ACL data by directing packets to
// the appropriate internal logical link handler;
//
// * Manages priority based scheduling.
//
// * Provides an API surface for L2CAP channel creation and logical link
// management bound to a single creation thread.
//
// There can be a single instance of ChannelManager for a HCI transport.
//
// THREAD-SAFETY:
//
// This object is not thread safe. Construction/destruction must happen on the
// thread where this is used.
class ChannelManager final {
public:
using LinkErrorCallback = fit::closure;
// Used to schedule a series of packets on the link type |ll_type| to be transmitted to the
// controller's ACL endpoint. All the packets in each invocation must be transmitted contiguously
// and in order. This will be called on the thread which the ChannelManager object is created, up
// to the object's duration.
using SendAclCallback =
fit::function<bool(LinkedList<hci::ACLDataPacket> packets, ChannelId channel_id,
hci::ACLDataChannel::PacketPriority priority)>;
// Used to drop stale queued ACL data packets for which |predicate| returns true (eg. when a
// channel is closed). Queued ACL data packets are those that were sent with |SendAclCallback| but
// not have not yet been transmitted to the controller.
using DropQueuedAclCallback = fit::function<void(hci::ACLPacketPredicate predicate)>;
// Creates L2CAP state for logical links and channels.
//
// |max_acl_payload_size| and |max_le_payload_size| are the "maximum size[s] of HCI ACL (excluding
// header) Data Packets... sent from the Host to the Controller" (Core v5.0 Vol 2, Part E, Section
// 4.1) used for fragmenting outbound data. Data that is fragmented will be passed contiguously as
// invocations of |send_packets_cb|.
//
// State changes are processed on |l2cap_dispatcher|.
ChannelManager(size_t max_acl_payload_size, size_t max_le_payload_size,
SendAclCallback send_acl_cb, DropQueuedAclCallback filter_acl_cb,
async_dispatcher_t* l2cap_dispatcher);
~ChannelManager();
// Returns a handler for data packets received from the Bluetooth controller bound to this object.
// It must be called from the creation thread, but it is safe to call past ChannelManager's
// lifetime.
hci::ACLPacketHandler MakeInboundDataHandler();
// Registers the ACL connection with the L2CAP layer. L2CAP channels can be
// opened on the logical link represented by |handle| after a call to this
// method.
//
// |link_error_callback| will be used to notify when a channel signals a link
// error.
//
// |security_callback| will be used to request an upgrade to the link security
// level. This can be triggered by dynamic L2CAP channel creation or by a
// service-level client via Channel::UpgradeSecurity().
//
// It is an error to register the same |handle| value more than once as either
// kind of channel without first unregistering it (asserted in debug builds).
void RegisterACL(hci::ConnectionHandle handle, hci::Connection::Role role,
LinkErrorCallback link_error_callback,
SecurityUpgradeCallback security_callback);
// Registers a LE connection with the L2CAP layer. L2CAP channels can be
// opened on the logical link represented by |handle| after a call to this
// method.
//
// |conn_param_callback| will be used to notify the caller if new connection
// parameters were accepted from the remote end of the link.
//
// |link_error_callback| will be used to notify when a channel signals a link
// error.
//
// |security_callback| will be used to request an upgrade to the link security
// level. This can be triggered by dynamic L2CAP channel creation or by a
// service-level client via Channel::UpgradeSecurity().
//
// It is an error to register the same |handle| value more than once as either
// kind of channel without first unregistering it (asserted in debug builds).
void RegisterLE(hci::ConnectionHandle handle, hci::Connection::Role role,
LEConnectionParameterUpdateCallback conn_param_callback,
LinkErrorCallback link_error_callback, SecurityUpgradeCallback security_callback);
// Removes a connection. All incoming data packets on this link will be dropped. If the
// connection was previously registered, all corresponding Channels will be closed.
//
// NOTE: It is recommended that a link entry be removed AFTER the controller
// sends a HCI Disconnection Complete Event for the corresponding logical
// link. This is to prevent incorrectly buffering data if the controller has
// more packets to send after removing the link entry.
void Unregister(hci::ConnectionHandle handle);
// Assigns the security level of a logical link. Has no effect if |handle| has
// not been previously registered or the link is closed.
void AssignLinkSecurityProperties(hci::ConnectionHandle handle, sm::SecurityProperties security);
// Opens the L2CAP fixed channel with |channel_id| over the logical link
// identified by |connection_handle| and starts routing packets. Returns
// nullptr if the channel is already open.
fbl::RefPtr<Channel> OpenFixedChannel(hci::ConnectionHandle connection_handle,
ChannelId channel_id);
// Opens an out-bound connection-oriented L2CAP channel on the link specified by |handle| to the
// requested |psm| with the preferred parameters |params|.
// Returns a channel asynchronously via |callback| (posted on the given |dispatcher|).
void OpenChannel(hci::ConnectionHandle handle, PSM psm, ChannelParameters params,
ChannelCallback cb, async_dispatcher_t* dispatcher);
// Register/Unregister a callback for incoming service connections.
// Incoming channels will be configured using using the preferred parameters |params|.
bool RegisterService(PSM psm, ChannelParameters params, ChannelCallback cb,
async_dispatcher_t* dispatcher);
void UnregisterService(PSM psm);
// Information stored and returned for registered services that is needed to configure and forward
// new channels for this service.
using ServiceInfo = ServiceInfo<ChannelCallback>;
// Returns mapping of ChannelId -> PacketPriority for use with ACLDataChannel::SendPacket.
static hci::ACLDataChannel::PacketPriority ChannelPriority(ChannelId id);
// Send a Connection Parameter Update Request on the LE signaling channel. When the Connection
// Parameter Update Response is received, |request_cb| will be called on |dispatcher| and passed
// the result, |accepted|.
// NOTE: The local Host must be an LE slave, and this request should only be sent if the slave or
// host does not support the Connection Parameters Request Link Layer Control Procedure (Core Spec
// v5.2, Vol 3, Part A, Sec 4.20).
void RequestConnectionParameterUpdate(hci::ConnectionHandle handle,
hci::LEPreferredConnectionParameters params,
ConnectionParameterUpdateRequestCallback request_cb,
async_dispatcher_t* dispatcher);
private:
// Called when an ACL data packet is received from the controller. This method
// is responsible for routing the packet to the corresponding LogicalLink.
void OnACLDataReceived(hci::ACLDataPacketPtr data_packet);
// Called by the various Register functions. Returns a pointer to the newly
// added link.
internal::LogicalLink* RegisterInternal(hci::ConnectionHandle handle,
hci::Connection::LinkType ll_type,
hci::Connection::Role role, size_t max_payload_size);
// If a service (identified by |psm|) requested has been registered, return a ServiceInfo object
// containing preferred channel parameters and a callback that passes an inbound channel to the
// registrant. The callback may be called repeatedly to pass multiple channels for |psm|, but
// should not be stored because the service may be unregistered at a later time. Calls for
// unregistered services return null.
std::optional<ServiceInfo> QueryService(hci::ConnectionHandle handle, PSM psm);
// Maximum sizes for data packet payloads from host to controller.
const size_t max_acl_payload_size_;
const size_t max_le_payload_size_;
// Queues data packets to be delivered to the controller for a given link type.
SendAclCallback send_acl_cb_;
// Drops data packets pending delivery to the controller.
DropQueuedAclCallback drop_queued_acl_cb_;
async_dispatcher_t* l2cap_dispatcher_;
using LinkMap = std::unordered_map<hci::ConnectionHandle, fbl::RefPtr<internal::LogicalLink>>;
LinkMap ll_map_;
// Stores packets received on a connection handle before a link for it has
// been created.
using PendingPacketMap =
std::unordered_map<hci::ConnectionHandle, LinkedList<hci::ACLDataPacket>>;
PendingPacketMap pending_packets_;
// Store information required to create and forward channels for locally-
// hosted services.
using ServiceMap = std::unordered_map<PSM, ServiceInfo>;
ServiceMap services_;
fxl::ThreadChecker thread_checker_;
fxl::WeakPtrFactory<ChannelManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelManager);
};
} // namespace l2cap
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_