| // 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_ |