blob: c9b49231ee2781bb23462f13773231ccfb4065ff [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 GARNET_DRIVERS_BLUETOOTH_LIB_L2CAP_CHANNEL_MANAGER_H_
#define GARNET_DRIVERS_BLUETOOTH_LIB_L2CAP_CHANNEL_MANAGER_H_
#include <memory>
#include <mutex>
#include <unordered_map>
#include <lib/async/dispatcher.h>
#include <zircon/compiler.h>
#include "garnet/drivers/bluetooth/lib/hci/acl_data_packet.h"
#include "garnet/drivers/bluetooth/lib/hci/connection.h"
#include "garnet/drivers/bluetooth/lib/hci/hci.h"
#include "garnet/drivers/bluetooth/lib/l2cap/channel.h"
#include "garnet/drivers/bluetooth/lib/l2cap/le_signaling_channel.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/synchronization/thread_checker.h"
namespace btlib {
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;
ChannelManager(fxl::RefPtr<hci::Transport> hci, async_dispatcher_t* l2cap_dispatcher);
~ChannelManager();
// 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. It will be posted onto |dispatcher|.
//
// |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().
//
// All callbacks will be posted onto |dispatcher|.
//
// 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,
async_dispatcher_t* dispatcher);
// 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().
//
// All callbacks will be posted onto |dispatcher|.
//
// 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).
using LEConnectionParameterUpdateCallback =
internal::LESignalingChannel::ConnectionParameterUpdateCallback;
void RegisterLE(hci::ConnectionHandle handle, hci::Connection::Role role,
LEConnectionParameterUpdateCallback conn_param_callback,
LinkErrorCallback link_error_callback,
SecurityUpgradeCallback security_callback,
async_dispatcher_t* dispatcher);
// Removes a previously registered connection. All corresponding Channels will
// be closed and all incoming data packets on this link will be dropped.
//
// 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);
// Open an out-bound connection-oriented L2CAP channel.
void OpenChannel(hci::ConnectionHandle handle, PSM psm, ChannelCallback cb,
async_dispatcher_t* dispatcher);
// Register/Unregister a callback for incoming service connections.
bool RegisterService(PSM psm, ChannelCallback cb,
async_dispatcher_t* dispatcher);
void UnregisterService(PSM psm);
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);
// If a service (identified by |psm|) requested has been registered, return 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 an empty callback.
ChannelCallback QueryService(hci::ConnectionHandle handle, PSM psm);
fxl::RefPtr<hci::Transport> hci_;
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,
common::LinkedList<hci::ACLDataPacket>>;
PendingPacketMap pending_packets_;
// Store information required to create and forward channels for locally-
// hosted services.
//
// TODO(NET-1240): Add desired configuration options
using ServiceMap = std::unordered_map<PSM, ChannelCallback>;
ServiceMap services_;
fxl::ThreadChecker thread_checker_;
fxl::WeakPtrFactory<ChannelManager> weak_ptr_factory_;
FXL_DISALLOW_COPY_AND_ASSIGN(ChannelManager);
};
} // namespace l2cap
} // namespace btlib
#endif // GARNET_DRIVERS_BLUETOOTH_LIB_L2CAP_CHANNEL_MANAGER_H_