blob: f0a9d4baf05b6ef8579f0fa243ea44b7bc1cbc7e [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_LOGICAL_LINK_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_LOGICAL_LINK_H_
#include <fbl/macros.h>
#include <fbl/ref_counted.h>
#include <lib/async/dispatcher.h>
#include <lib/fit/function.h>
#include <zircon/compiler.h>
#include <list>
#include <memory>
#include <mutex>
#include <unordered_map>
#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/hci/transport.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/dynamic_channel_registry.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fragmenter.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/recombiner.h"
#include "src/lib/fxl/synchronization/thread_checker.h"
namespace bt {
namespace l2cap {
namespace internal {
class ChannelImpl;
class LESignalingChannel;
class SignalingChannel;
// Represents a controller logical link. Each instance aids in mapping L2CAP
// channels to their corresponding controller logical link and vice versa.
// Instances are created and owned by a ChannelManager.
class LogicalLink final : public fbl::RefCounted<LogicalLink> {
public:
// Used to query if ChannelManager has a service registered (identified by
// |psm|). If it does, return a function that can be used to provide the
// registrant with channels opened for the service. Otherwise, return nullptr.
using QueryServiceCallback =
fit::function<ChannelCallback(hci::ConnectionHandle handle, PSM psm)>;
// Constructs a new LogicalLink and initializes the signaling fixed channel.
static fbl::RefPtr<LogicalLink> New(hci::ConnectionHandle handle,
hci::Connection::LinkType type,
hci::Connection::Role role,
async_dispatcher_t* dispatcher,
fxl::RefPtr<hci::Transport> hci,
QueryServiceCallback query_service_cb);
// Notifies and closes all open channels on this link. This must be called to
// cleanly shut down a LogicalLink. WARNING: Failure to do so will cause the
// memory to leak since associated channels hold a reference back to the link.
//
// The link MUST not be closed when this is called.
void Close();
// Opens the channel with |channel_id| over this logical link. See channel.h
// for documentation on |rx_callback| and |closed_callback|. Returns nullptr
// if a Channel for |channel_id| already exists.
//
// The link MUST not be closed when this is called.
fbl::RefPtr<Channel> OpenFixedChannel(ChannelId channel_id);
// Opens a dynamic channel to the requested |psm| and returns a channel
// asynchronously via |callback| (posted on the given |dispatcher|).
//
// The link MUST not be closed when this is called.
void OpenChannel(PSM psm, ChannelCallback callback,
async_dispatcher_t* dispatcher);
// Takes ownership of |packet| for PDU processing and routes it to its target
// channel. This must be called on the HCI I/O thread.
//
// The link MUST not be closed when this is called.
void HandleRxPacket(hci::ACLDataPacketPtr packet);
// Sends a B-frame PDU out over the ACL data channel, where |payload| is the
// B-frame information payload. |remote_id| identifies the destination peer's
// L2CAP channel endpoint for this frame. This must be called on the creation
// thread.
//
// It is safe to call this function on a closed link; it will have no effect.
void SendBasicFrame(ChannelId remote_id, const ByteBuffer& payload);
// Requests a security upgrade using the registered security upgrade callback.
// Invokes the |callback| argument with the result of the operation.
// |callback| will be run by the requested |dispatcher|.
//
// Has no effect if the link is closed.
void UpgradeSecurity(sm::SecurityLevel level, sm::StatusCallback callback,
async_dispatcher_t* dispatcher);
// Assigns the security level of this link and resolves pending security
// upgrade requests. Has no effect if the link is closed.
void AssignSecurityProperties(const sm::SecurityProperties& security);
// Assigns the link error callback to be invoked when a channel signals a link
// error.
void set_error_callback(fit::closure callback,
async_dispatcher_t* dispatcher);
// Assigns the security upgrade delegate for this link.
void set_security_upgrade_callback(SecurityUpgradeCallback callback,
async_dispatcher_t* dispatcher);
// Returns the dispatcher that this LogicalLink operates on.
async_dispatcher_t* dispatcher() const { return dispatcher_; }
hci::Connection::LinkType type() const { return type_; }
hci::Connection::Role role() const { return role_; }
hci::ConnectionHandle handle() const { return handle_; }
const sm::SecurityProperties security() {
std::lock_guard<std::mutex> lock(mtx_);
return security_;
}
// Returns the LE signaling channel implementation or nullptr if this is not a
// LE-U link.
LESignalingChannel* le_signaling_channel() const;
private:
friend class ChannelImpl;
friend fbl::RefPtr<LogicalLink>;
LogicalLink(hci::ConnectionHandle handle, hci::Connection::LinkType type,
hci::Connection::Role role, async_dispatcher_t* dispatcher,
fxl::RefPtr<hci::Transport> hci,
QueryServiceCallback query_service_cb);
// Initializes the fragmenter, the fixed signaling channel, and the dynamic
// channel registry based on the link type. Called by the factory method
// "New()".
void Initialize();
// When a logical link is destroyed it notifies all of its channels to close
// themselves. Data packets will no longer be routed to the associated
// channels.
~LogicalLink();
bool AllowsFixedChannel(ChannelId id);
// Called by ChannelImpl::Deactivate(). Removes the channel from the given
// link.
//
// Does nothing if the link is closed.
void RemoveChannel(Channel* chan);
// Called by ChannelImpl::SignalLinkError(). Has no effect if the link is
// closed.
void SignalError();
// If the service identified by |psm| can be opened, return a function to
// complete the channel open for a newly-opened DynamicChannel. Otherwise,
// return nullptr.
//
// This MUST not be called on a closed link.
DynamicChannelRegistry::DynamicChannelCallback OnServiceRequest(PSM psm);
// Called by |dynamic_registry_| when the peer requests the closure of a
// dynamic channel using a signaling PDU.
//
// This MUST not be called on a closed link.
void OnChannelDisconnectRequest(const DynamicChannel* dyn_chan);
// Given a newly-opened dynamic channel as reported by this link's
// DynamicChannelRegistry, create a ChannelImpl for it to carry user data,
// then pass a pointer to it through |open_cb| on |dispatcher|. If |dyn_chan|
// is null, then pass nullptr into |open_cb|.
//
// This MUST not be called on a closed link.
void CompleteDynamicOpen(const DynamicChannel* dyn_chan,
ChannelCallback open_cb,
async_dispatcher_t* dispatcher);
// Members that can be accessed from any thread.
std::mutex mtx_;
sm::SecurityProperties security_ __TA_GUARDED(mtx_);
// All members below must be accessed on the L2CAP dispatcher thread.
fxl::RefPtr<hci::Transport> hci_;
async_dispatcher_t* dispatcher_;
// Information about the underlying controller logical link.
hci::ConnectionHandle handle_;
hci::Connection::LinkType type_;
hci::Connection::Role role_;
fit::closure link_error_cb_;
async_dispatcher_t* link_error_dispatcher_;
SecurityUpgradeCallback security_callback_;
async_dispatcher_t* security_dispatcher_;
// No data packets are processed once this gets set to true.
bool closed_;
// Owns and manages the L2CAP signaling channel on this logical link.
// Depending on |type_| this will either implement the LE or BR/EDR signaling
// commands.
std::unique_ptr<SignalingChannel> signaling_channel_;
// Fragmenter and Recombiner are always accessed on the L2CAP thread.
Fragmenter fragmenter_;
Recombiner recombiner_;
// Channels that were created on this link. Channels notify the link for
// removal when deactivated.
using ChannelMap = std::unordered_map<ChannelId, fbl::RefPtr<ChannelImpl>>;
ChannelMap channels_;
// Stores packets that have been received on a currently closed channel. We
// buffer these for fixed channels so that the data is available when the
// channel is opened.
using PendingPduMap = std::unordered_map<ChannelId, std::list<PDU>>;
PendingPduMap pending_pdus_;
// Dynamic channels opened with the remote. The registry is destroyed and all
// procedures terminated when this link gets closed.
std::unique_ptr<DynamicChannelRegistry> dynamic_registry_;
QueryServiceCallback query_service_cb_;
fxl::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LogicalLink);
};
} // namespace internal
} // namespace l2cap
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_LOGICAL_LINK_H_