| // 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 <lib/async/dispatcher.h> |
| #include <lib/fit/function.h> |
| #include <lib/fit/promise.h> |
| #include <lib/fit/thread_checker.h> |
| #include <lib/sys/inspect/cpp/component.h> |
| #include <lib/trace/event.h> |
| #include <zircon/compiler.h> |
| |
| #include <list> |
| #include <memory> |
| #include <mutex> |
| #include <unordered_map> |
| |
| #include <fbl/macros.h> |
| #include <fbl/ref_counted.h> |
| |
| #include "lib/fit/result.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/inspectable.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.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_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/transport.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/bredr_command_handler.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/channel_manager.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_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/low_energy_command_handler.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/recombiner.h" |
| |
| namespace bt::l2cap::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. This owns each link's signaling fixed |
| // channel and the dynamic channel logic that operates on that channel. A LogicalLink must be |
| // explicitly `Close`d before destruction, and will assert on this behavior in its destructor. |
| // |
| // Instances are created and owned by a ChannelManager. |
| class LogicalLink final : public fbl::RefCounted<LogicalLink> { |
| public: |
| // Returns a function that accepts opened channels for a registered local service identified by |
| // |psm| on a given connection identified by |handle|, or nullptr if there is no service |
| // registered for that PSM. |
| using QueryServiceCallback = fit::function<std::optional<ChannelManager::ServiceInfo>( |
| hci::ConnectionHandle handle, PSM psm)>; |
| |
| // Constructs a new LogicalLink and initializes the signaling fixed channel. |
| // |dispatcher| will be used for all Channels created on this link. |
| // |max_payload_size| shall be the maximum "host to controller" data packet payload size for the |
| // link |type|, per Core Spec v5.0 Vol 2, Part E, Sec 4.1. |
| // Both |query_service_cb| and the inbound channel delivery callback that it returns will be |
| // executed on this object's creation thread. |
| // If |random_channel_ids| is true, assign dynamic channels randomly instead of |
| // starting at the beginning of the dynamic channel range. |
| static fbl::RefPtr<LogicalLink> New(hci::ConnectionHandle handle, hci::Connection::LinkType type, |
| hci::Connection::Role role, fit::executor* executor, |
| size_t max_payload_size, |
| QueryServiceCallback query_service_cb, |
| hci::AclDataChannel* acl_data_channel, |
| bool random_channel_ids); |
| |
| // 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| with the preferred parameters |params| and |
| // returns a channel asynchronously via |callback|. |
| // |
| // The link MUST not be closed when this is called. |
| void OpenChannel(PSM psm, ChannelParameters params, ChannelCallback callback); |
| |
| // Takes ownership of |packet| for PDU processing and routes it to its target |
| // channel. This must be called on this object's creation thread. |
| // |
| // The link MUST not be closed when this is called. |
| void HandleRxPacket(hci::ACLDataPacketPtr packet); |
| |
| // Sends a PDU out over the ACL data channel, where |payload| is the contents following the Basic |
| // L2CAP header and preceding the Frame Check Sequence (FCS; if enabled with |fcs_option|). Frame |
| // formats are defined in Core Spec v5.0, Vol 3, Part A, Section 3. |
| // |
| // |remote_id| identifies the peer's L2CAP channel endpoint for this frame. This must be called on |
| // the creation thread. |
| // |
| // |flushable| indicates whether the PDU should be marked as flushable (the controller can |
| // flush the PDU after the automatic flush timeout). |
| // |
| // It is safe to call this function on a closed link; it will have no effect. |
| void SendFrame(ChannelId remote_id, const ByteBuffer& payload, |
| FrameCheckSequenceOption fcs_option, bool flushable); |
| |
| // 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); |
| |
| // Send a Connection Parameter Update Request on the LE signaling channel. When the Connection |
| // Parameter Update Response is received, |request_cb| will be called with the result, |accepted|. |
| // NOTE: the local Host must be an LE slave. |
| void SendConnectionParameterUpdateRequest(hci::LEPreferredConnectionParameters params, |
| ConnectionParameterUpdateRequestCallback request_cb); |
| |
| // Request a change of this link's ACL priority to |priority|. |
| // |channel| must indicate the channel making the request. |
| // |callback| will be called with the result of the request. |
| // The request will fail if |priority| conflicts with another channel's priority or the |
| // controller does not support changing the ACL priority. |
| // |
| // Requests are queued and handled sequentially in order to prevent race conditions. |
| void RequestAclPriority(Channel* channel, hci::AclPriority priority, |
| fit::callback<void(fit::result<>)> callback); |
| |
| // Sets an automatic flush timeout with duration |flush_timeout|. |callback| will be called with |
| // the result of the operation. This is only supported if the link type is kACL (BR/EDR). |
| // |flush_timeout| must be in the range [1ms - hci::kMaxAutomaticFlushTimeoutDuration]. A flush |
| // timeout of zx::duration::infinite() indicates an infinite flush timeout (no automatic flush), |
| // the default. |
| void SetBrEdrAutomaticFlushTimeout( |
| zx::duration flush_timeout, fit::callback<void(fit::result<void, hci::StatusCode>)> callback); |
| |
| // Attach LogicalLink's inspect node as a child of |parent| with the given |name|. |
| void AttachInspect(inspect::Node& parent, std::string name); |
| |
| // Returns mapping of ChannelId -> PacketPriority for use with AclDataChannel::SendPacket. |
| static hci::AclDataChannel::PacketPriority ChannelPriority(ChannelId id); |
| |
| // Assigns the link error callback to be invoked when a channel signals a link |
| // error. |
| void set_error_callback(fit::closure callback); |
| |
| // Assigns the security upgrade delegate for this link. |
| void set_security_upgrade_callback(SecurityUpgradeCallback callback); |
| |
| // Assigns the callback to be invoked when a valid Connection Parameter Update Request is received |
| // on the signaling channel. |
| void set_connection_parameter_update_callback(LEConnectionParameterUpdateCallback callback); |
| |
| hci::Connection::LinkType type() const { return type_; } |
| hci::Connection::Role role() const { return role_; } |
| hci::ConnectionHandle handle() const { return handle_; } |
| |
| const sm::SecurityProperties security() { return security_; } |
| |
| // Returns the LE signaling channel implementation or nullptr if this is not a |
| // LE-U link. |
| LESignalingChannel* le_signaling_channel() const; |
| |
| fxl::WeakPtr<LogicalLink> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } |
| |
| private: |
| friend class ChannelImpl; |
| friend fbl::RefPtr<LogicalLink>; |
| |
| LogicalLink(hci::ConnectionHandle handle, hci::Connection::LinkType type, |
| hci::Connection::Role role, fit::executor* executor, size_t max_acl_payload_size, |
| QueryServiceCallback query_service_cb, hci::AclDataChannel* acl_data_channel); |
| |
| // 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(bool random_channel_ids); |
| |
| // 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(); |
| |
| // Returns true if |id| is valid and supported by the peer. |
| bool AllowsFixedChannel(ChannelId id); |
| |
| // Called by ChannelImpl::Deactivate(). Removes the channel from the given link. Returned promise |
| // yields fit::ok when channel no longer exists. |
| fit::promise<> RemoveChannel(Channel* chan); |
| |
| // Called by ChannelImpl::SignalLinkError() to disconnect all channels then signal an error to the |
| // lower layers (usually GAP, to request a link disconnection). 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. |
| std::optional<DynamicChannelRegistry::ServiceInfo> 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|. 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); |
| |
| // Send an Information Request signaling packet of type Fixed Channels Supported. |
| void SendFixedChannelsSupportedInformationRequest(); |
| |
| // Handler for Information Response signaling packet. This is used to handle the Fixed Channels |
| // Supported information response, which indicates which fixed channels the peer supports (Core |
| // Spec v5.1, Vol 3, Part A, Sec 4.13). Except for the signaling channels, fixed channels may not |
| // be created until this response has been received. |
| // TODO(fxbug.dev/43668): save fixed channels mask and use to verify opened fixed channel ids are |
| // supported |
| void OnRxFixedChannelsSupportedInfoRsp(const BrEdrCommandHandler::InformationResponse& rsp); |
| |
| // Start serving Connection Parameter Update Requests on the LE signaling channel. |
| void ServeConnectionParameterUpdateRequest(); |
| |
| // Handler called when a Connection Parameter Update Request is received on the LE signaling |
| // channel. |
| void OnRxConnectionParameterUpdateRequest( |
| uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency, |
| uint16_t timeout_multiplier, |
| LowEnergyCommandHandler::ConnectionParameterUpdateResponder* responder); |
| |
| // Processes the next ACL priority request in the |pending_acl_requests_| queue. |
| // In order to optimize radio performance, ACL priority is downgraded whenever possible (i.e. when |
| // no more channels are requesting high priority). |
| void HandleNextAclPriorityRequest(); |
| |
| sm::SecurityProperties security_; |
| |
| // Information about the underlying controller logical link. |
| hci::ConnectionHandle handle_; |
| hci::Connection::LinkType type_; |
| hci::Connection::Role role_; |
| |
| // The duration after which BR/EDR packets are flushed from the controller. |
| // By default, the flush timeout is infinite (no automatic flush). |
| UintInspectable<zx::duration> flush_timeout_; |
| |
| fit::closure link_error_cb_; |
| |
| SecurityUpgradeCallback security_callback_; |
| |
| LEConnectionParameterUpdateCallback connection_parameter_update_callback_; |
| |
| // 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. |
| const 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_; |
| |
| struct PendingAclRequest { |
| fbl::RefPtr<ChannelImpl> channel; |
| hci::AclPriority priority; |
| fit::callback<void(fit::result<>)> callback; |
| }; |
| std::queue<PendingAclRequest> pending_acl_requests_; |
| |
| // 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_; |
| |
| hci::AclDataChannel* acl_data_channel_; |
| |
| // Search function for inbound service requests. Returns handler that accepts opened channels. |
| QueryServiceCallback query_service_cb_; |
| |
| struct InspectProperties { |
| inspect::Node node; |
| inspect::Node channels_node; |
| inspect::StringProperty handle; |
| inspect::StringProperty link_type; |
| }; |
| InspectProperties inspect_properties_; |
| |
| fit::executor* const executor_; |
| |
| fit::thread_checker thread_checker_; |
| |
| fxl::WeakPtrFactory<LogicalLink> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LogicalLink); |
| }; |
| |
| } // namespace bt::l2cap::internal |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_LOGICAL_LINK_H_ |