| // 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_MANAGER_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_ |
| |
| #include <lib/async/dispatcher.h> |
| #include <lib/fit/thread_checker.h> |
| #include <lib/sys/inspect/cpp/component.h> |
| #include <lib/trace/event.h> |
| #include <zircon/compiler.h> |
| |
| #include <memory> |
| #include <mutex> |
| #include <unordered_map> |
| |
| #include <fbl/macros.h> |
| |
| #include "lib/async/cpp/executor.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.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/l2cap/channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/le_signaling_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h" |
| |
| namespace bt { |
| |
| 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; |
| |
| // Creates L2CAP state for logical links and channels. |
| // |
| // |max_acl_payload_size| and |max_le_payload_size| are the "maximum size[s] of HCI ACL |
| // (excluding header) Data Packets... sent from the Host to the Controller" (Core v5.0 Vol 2, |
| // Part E, Section 4.1) used for fragmenting outbound data. Data that is fragmented will be |
| // passed contiguously as invocations of |acl_data_channel->SendPackets()|. |
| ChannelManager(size_t max_acl_payload_size, size_t max_le_payload_size, |
| hci::AclDataChannel* acl_data_channel, bool random_channel_ids); |
| ~ChannelManager(); |
| |
| // Returns a handler for data packets received from the Bluetooth controller bound to this object. |
| // It must be called from the creation thread, but it is safe to call past ChannelManager's |
| // lifetime. |
| hci::ACLPacketHandler MakeInboundDataHandler(); |
| |
| // 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. |
| // |
| // |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(). |
| // |
| // 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); |
| |
| // 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(). |
| // |
| // 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 RegisterLE(hci::ConnectionHandle handle, hci::Connection::Role role, |
| LEConnectionParameterUpdateCallback conn_param_callback, |
| LinkErrorCallback link_error_callback, SecurityUpgradeCallback security_callback); |
| |
| // Removes a connection. All incoming data packets on this link will be dropped. If the |
| // connection was previously registered, all corresponding Channels will be closed. |
| // |
| // 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); |
| |
| // Opens an out-bound connection-oriented L2CAP channel on the link specified by |handle| to the |
| // requested |psm| with the preferred parameters |params|. |
| // Returns a channel asynchronously via |callback|. |
| void OpenChannel(hci::ConnectionHandle handle, PSM psm, ChannelParameters params, |
| ChannelCallback cb); |
| |
| // Register/Unregister a callback for incoming service connections. |
| // Incoming channels will be configured using using the preferred parameters |params|. |
| bool RegisterService(PSM psm, ChannelParameters params, ChannelCallback cb); |
| void UnregisterService(PSM psm); |
| |
| // Information stored and returned for registered services that is needed to configure and forward |
| // new channels for this service. |
| using ServiceInfo = ServiceInfo<ChannelCallback>; |
| |
| // 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, and this request should only be sent if the slave or |
| // host does not support the Connection Parameters Request Link Layer Control Procedure (Core Spec |
| // v5.2, Vol 3, Part A, Sec 4.20). |
| void RequestConnectionParameterUpdate(hci::ConnectionHandle handle, |
| hci::LEPreferredConnectionParameters params, |
| ConnectionParameterUpdateRequestCallback request_cb); |
| |
| // Attach ChannelManager's inspect nodes as children of |parent|. |
| void AttachInspect(inspect::Node& parent); |
| |
| // Returns a pointer to the internal LogicalLink with the corresponding link |handle|, or nullptr |
| // if none exists. |
| // NOTE: This is intended ONLY for unit tests. Clients should use the other public methods to |
| // interact with the link. |
| fxl::WeakPtr<internal::LogicalLink> LogicalLinkForTesting(hci::ConnectionHandle handle); |
| |
| 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, size_t max_payload_size); |
| |
| // If a service (identified by |psm|) requested has been registered, return a ServiceInfo object |
| // containing preferred channel parameters and 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 null. |
| std::optional<ServiceInfo> QueryService(hci::ConnectionHandle handle, PSM psm); |
| |
| // Maximum sizes for data packet payloads from host to controller. |
| const size_t max_acl_payload_size_; |
| const size_t max_le_payload_size_; |
| |
| hci::AclDataChannel* acl_data_channel_; |
| |
| using LinkMap = std::unordered_map<hci::ConnectionHandle, fbl::RefPtr<internal::LogicalLink>>; |
| LinkMap ll_map_; |
| inspect::Node ll_node_; |
| |
| // Stores packets received on a connection handle before a link for it has |
| // been created. |
| using PendingPacketMap = |
| std::unordered_map<hci::ConnectionHandle, LinkedList<hci::ACLDataPacket>>; |
| PendingPacketMap pending_packets_; |
| |
| // Store information required to create and forward channels for locally- |
| // hosted services. |
| struct ServiceData { |
| void AttachInspect(inspect::Node& parent); |
| ServiceInfo info; |
| PSM psm; |
| inspect::Node node; |
| inspect::StringProperty psm_property; |
| }; |
| using ServiceMap = std::unordered_map<PSM, ServiceData>; |
| ServiceMap services_; |
| inspect::Node services_node_; |
| |
| // Stored info on whether random channel ids are requested. |
| bool random_channel_ids_; |
| |
| // TODO(fxbug.rev/63851): Find a better home for this. For now, we know that this only holds |
| // promises scheduled by LogicalLinks to destroy themselves, so this living here provides a |
| // minimal guarantee that the executor outlives the LogicalLinks. |
| async::Executor executor_; |
| |
| fit::thread_checker thread_checker_; |
| fxl::WeakPtrFactory<ChannelManager> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelManager); |
| }; |
| |
| } // namespace l2cap |
| } // namespace bt |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_ |