blob: 59bc822be64774ee12033773281cd10c93773aee [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_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_H_
#include <lib/async/dispatcher.h>
#include <lib/fit/function.h>
#include <lib/zx/socket.h>
#include <zircon/compiler.h>
#include <atomic>
#include <climits>
#include <list>
#include <memory>
#include <mutex>
#include <queue>
#include <fbl/macros.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/pdu.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/rx_engine.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/tx_engine.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/status.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/types.h"
#include "src/lib/fxl/synchronization/thread_checker.h"
namespace bt {
namespace l2cap {
// Represents a L2CAP channel. Each instance is owned by a service
// implementation that operates on the corresponding channel. Instances can only
// be obtained from a ChannelManager.
//
// A Channel can operate in one of 6 L2CAP Modes of Operation (see Core Spec
// v5.0, Vol 3, Part A, Section 2.4). Only Basic Mode is currently supported.
//
// USAGE:
//
// Channel is an abstract base class. There are two concrete implementations:
//
// * internal::ChannelImpl (defined below) which implements a real L2CAP
// channel. Instances are obtained from ChannelManager and tied to
// internal::LogicalLink instances.
//
// * FakeChannel, which can be used for unit testing service-layer entities
// that operate on one or more L2CAP channel(s).
//
// Production instances are obtained from a ChannelManager. Channels are safe to
// activate, deactivate, and destroy on any thread.
//
// A Channel's owner must explicitly call Deactivate() and must not rely on
// dropping its reference to close the channel.
//
// When a LogicalLink closes, all of its active channels become deactivated
// when it closes and this is signaled by running the ClosedCallback passed to
// ActivateWithDispatcher() or ActivateOnDataDomain.
class Channel : public fbl::RefCounted<Channel> {
public:
// Identifier for this channel's endpoint on this device. It can be prior-
// specified for fixed channels or allocated for dynamic channels per v5.0,
// Vol 3, Part A, Section 2.1 "Channel Identifiers." Channels on a link will
// have unique identifiers to each other.
ChannelId id() const { return id_; }
// Identifier for this channel's endpoint on the remote peer. Same value as
// |id()| for fixed channels and allocated by the remote for dynamic channels.
ChannelId remote_id() const { return remote_id_; }
// The type of the logical link this channel operates on.
hci::Connection::LinkType link_type() const { return link_type_; }
// The connection handle of the underlying logical link.
hci::ConnectionHandle link_handle() const { return link_handle_; }
// Returns a value that's unique for any channel connected to this device.
// If two channels have different unique_ids, they represent different
// channels even if their ids match.
using UniqueId = uint32_t;
UniqueId unique_id() const {
static_assert(sizeof(UniqueId) >= sizeof(hci::ConnectionHandle) + sizeof(ChannelId),
"UniqueId needs to be large enough to make unique IDs");
return (link_handle() << (sizeof(ChannelId) * CHAR_BIT)) | id();
}
ChannelMode mode() const { return info().mode; }
// These accessors define the concept of a Maximum Transmission Unit (MTU) as a maximum inbound
// (rx) and outbound (tx) packet size for the L2CAP implementation (see v5.2, Vol. 3, Part A
// 5.1). L2CAP requires that channel MTUs are at least 23 bytes for LE-U links and 48 bytes for
// ACL-U links. A further requirement is that "[t]he minimum MTU for a channel is the larger of
// the L2CAP minimum [...] and any MTU explicitly required by the protocols and profiles using
// that channel." `max_rx_sdu_size` is always determined by the capabilities of the local
// implementation. For dynamic channels, `max_tx_sdu_size` is determined through a configuration
// procedure with the peer (v5.2 Vol. 3 Part A 7.1). For fixed channels, this is always the
// maximum allowable L2CAP packet size, not a protocol-specific MTU.
uint16_t max_rx_sdu_size() const { return info().max_rx_sdu_size; }
uint16_t max_tx_sdu_size() const { return info().max_tx_sdu_size; }
// Returns the current configuration parameters for this channel.
ChannelInfo info() const { return info_; }
// Returns the current link security properties of the underlying link.
// Returns the lowest security level if the link is closed.
virtual const sm::SecurityProperties security() = 0;
// Callback invoked when this channel has been closed without an explicit
// request from the owner of this instance. For example, this can happen when
// the remote end closes a dynamically configured channel or when the
// underlying logical link is terminated through other means.
using ClosedCallback = fit::closure;
// Callback invoked when a new packet is received on this channel. Any
// previously buffered packets will be sent to |rx_cb| right away, provided
// that |rx_cb| is not empty and the underlying logical link is active.
using RxCallback = fit::function<void(ByteBufferPtr packet)>;
// Activates this channel assigning |dispatcher| to execute |rx_callback| and |closed_callback|.
//
// Returns false if the channel's link has been closed.
//
// Each channel can be activated only once.
//
// NOTE: Callers shouldn't assume that this method will succeed, as the underlying link can be
// removed at any time.
virtual bool ActivateWithDispatcher(RxCallback rx_callback, ClosedCallback closed_callback,
async_dispatcher_t* dispatcher) = 0;
// Activates this channel as in ActivateWithDispatcher, but callbacks will be executed immediately
// on the Data domain as L2CAP is notified of their underlying events.
//
// Any inbound data that has already been buffered for this channel will be drained by calling
// |rx_callback| repeatedly, before this call returns.
//
// Execution of |rx_callback| may block L2CAP data routing, so care should be taken to avoid
// introducing excessive latency.
//
// Each channel can be activated only once.
virtual bool ActivateOnDataDomain(RxCallback rx_callback, ClosedCallback closed_callback) = 0;
// Deactivates this channel. No more packets can be sent or received after
// this is called. |rx_callback| may still be called if it has been already
// dispatched to its task runner.
//
// This method is idempotent.
virtual void Deactivate() = 0;
// Signals that the underlying link should be disconnected. This should be
// called when a service layer protocol error requires the connection to be
// severed.
//
// The link error callback (provided to L2CAP::Register* methods) is invoked
// as a result of this operation. The handler is responsible for actually
// disconnecting the link.
//
// This does not deactivate the channel, though the channel is expected to
// close when the link gets removed later.
virtual void SignalLinkError() = 0;
// Requests to upgrade the security properties of the underlying link to the requested |level|
// and reports the result via |callback|, run on |dispatcher|. Has no effect if the channel is
// not active.
virtual void UpgradeSecurity(sm::SecurityLevel level, sm::StatusCallback callback,
async_dispatcher_t* dispatcher) = 0;
// Queue the given SDU payload for transmission over this channel, taking
// ownership of |sdu|. Returns true if the SDU was queued successfully, and
// false otherwise.
//
// For reasons why queuing might fail, see the documentation for the relevant
// TxEngine's QueueSdu() method. Note: a successfully enqueued SDU may still
// fail to reach the receiver, due to asynchronous local errors, transmission
// failure, or remote errors.
virtual bool Send(ByteBufferPtr sdu) = 0;
protected:
friend class fbl::RefPtr<Channel>;
// TODO(1022): define a preferred MTU somewhere
Channel(ChannelId id, ChannelId remote_id, hci::Connection::LinkType link_type,
hci::ConnectionHandle link_handle, ChannelInfo info);
virtual ~Channel() = default;
const ChannelId id_;
const ChannelId remote_id_;
const hci::Connection::LinkType link_type_;
const hci::ConnectionHandle link_handle_;
const ChannelInfo info_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Channel);
};
// A socket connected to an L2cap channel and its parameters
// params will be non-empty iff socket != ZX_INVALID_HANDLE
struct ChannelSocket {
ChannelSocket() : socket(zx::socket()), params(std::nullopt) {}
ChannelSocket(zx::socket socket, std::optional<ChannelInfo> params)
: socket(std::move(socket)), params(params) {
ZX_ASSERT(this->socket.is_valid() && this->params.has_value() ||
!this->socket.is_valid() && !this->params.has_value());
}
ChannelSocket(ChannelSocket&&) = default;
bool is_valid() const { return socket.is_valid(); }
explicit operator bool() const { return is_valid(); }
zx::socket socket;
std::optional<const ChannelInfo> params;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelSocket);
};
namespace internal {
class LogicalLink;
// Channel implementation used in production.
class ChannelImpl : public Channel {
public:
// Many core-spec protocols which operate over fixed channels (e.g. v5.2 Vol. 3 Parts F (ATT) and
// H (SMP)) define service-specific MTU values. Channels created with `CreateFixedChannel` do not
// check against these service-specific MTUs. Thus `bt-host` local services which operate over
// fixed channels are required to respect their MTU internally by:
// 1.) never sending packets larger than their spec-defined MTU.
// 2.) handling inbound PDUs which are larger than their spec-defined MTU appropriately.
static fbl::RefPtr<ChannelImpl> CreateFixedChannel(ChannelId id,
fxl::WeakPtr<internal::LogicalLink> link);
static fbl::RefPtr<ChannelImpl> CreateDynamicChannel(ChannelId id, ChannelId peer_id,
fxl::WeakPtr<internal::LogicalLink> link,
ChannelInfo info);
// Called by |link_| to notify us when the channel can no longer process data.
// This MUST NOT call any locking methods of |link_| as that WILL cause a
// deadlock.
void OnClosed();
// Called by |link_| when a PDU targeting this channel has been received.
// Contents of |pdu| will be moved.
void HandleRxPdu(PDU&& pdu);
// Channel overrides:
const sm::SecurityProperties security() override;
bool ActivateWithDispatcher(RxCallback rx_callback, ClosedCallback closed_callback,
async_dispatcher_t* dispatcher) override;
bool ActivateOnDataDomain(RxCallback rx_callback, ClosedCallback closed_callback) override;
void Deactivate() override;
void SignalLinkError() override;
bool Send(ByteBufferPtr sdu) override;
void UpgradeSecurity(sm::SecurityLevel level, sm::StatusCallback callback,
async_dispatcher_t* dispatcher) override;
private:
friend class fbl::RefPtr<ChannelImpl>;
ChannelImpl(ChannelId id, ChannelId remote_id, fxl::WeakPtr<internal::LogicalLink> link,
ChannelInfo info);
~ChannelImpl() override = default;
// TODO(armansito): Add MPS fields when we support segmentation/flow-control.
std::mutex mtx_;
bool active_ __TA_GUARDED(mtx_);
async_dispatcher_t* dispatcher_ __TA_GUARDED(mtx_);
RxCallback rx_cb_ __TA_GUARDED(mtx_);
ClosedCallback closed_cb_ __TA_GUARDED(mtx_);
// The LogicalLink that this channel is associated with. A channel is always
// created by a LogicalLink.
//
// |link_| is guaranteed to be valid as long as the link is active. This is
// because when a LogicalLink is torn down, it will notify all of its
// associated channels by calling OnLinkClosed() which sets |link_| to
// nullptr.
fxl::WeakPtr<internal::LogicalLink> link_ __TA_GUARDED(mtx_);
// The engine which processes received PDUs, and converts them to SDUs for
// upper layers.
std::unique_ptr<RxEngine> rx_engine_ __TA_GUARDED(mtx_);
// The engine which accepts SDUs, and converts them to PDUs for lower layers.
std::unique_ptr<TxEngine> tx_engine_ __TA_GUARDED(mtx_);
// The pending SDUs on this channel. Received PDUs are buffered if |rx_cb_| is
// currently not set.
// TODO(armansito): We should avoid STL containers for data packets as they
// all implicitly allocate. This is a reminder to fix this elsewhere
// (especially in the HCI layer).
std::queue<ByteBufferPtr, std::list<ByteBufferPtr>> pending_rx_sdus_ __TA_GUARDED(mtx_);
fxl::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelImpl);
};
} // namespace internal
} // namespace l2cap
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_H_